From 1337b6bceb79a92b5e3649c91e0e3a656cf540f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20=C3=81lvarez?= Date: Wed, 16 Feb 2022 23:09:13 -0400 Subject: [PATCH] feat(cache): Added PbCache class and enabled cache for controllers / close #32 --- CHANGELOG.md | 14 +- src/Controllers/Config/PbConfigController.php | 8 +- .../Navigation/PbNavigationController.php | 27 +- src/Controllers/PbBuilderController.php | 395 +++++++++++++----- .../Permission/PbPermissionController.php | 153 +++++-- src/Controllers/Role/PbRoleController.php | 116 ++++- src/Controllers/User/PbUserController.php | 135 +++++- src/Database/Seeders/PbConfigSeeder.php | 1 + src/Facades/PbDebugbarFacade.php | 13 + src/Helpers/helpers.php | 45 +- src/Interfaces/PbModelCrudInterface.php | 7 + .../PbIsDebugModeEnabledMiddleware.php | 4 +- src/Models/PbBuilder.php | 3 +- src/Overrides/Classes/PbDebugbar.php | 72 +++- src/Providers/PbAppServiceProvider.php | 6 +- src/Traits/PbControllerListingTrait.php | 4 +- src/Traits/PbControllerTrait.php | 32 +- src/Utilities/PbCache.php | 146 +++++++ src/Utilities/PbShares.php | 4 +- 19 files changed, 964 insertions(+), 221 deletions(-) create mode 100644 src/Facades/PbDebugbarFacade.php create mode 100644 src/Utilities/PbCache.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 27ed014..adfbc13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,18 +5,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [1.3.7] - 2022-02-13 +## [1.3.7.1] - 2022-02-14 + +- Implemented cache for controllers +- Added facades por ```PbDebugbar``` class + +## [1.3.7] - 2022-02-14 - BREAKING CHANGES: - [x] [1.3.6.1] - Added Middleware to store config data in session -- Helpers renamed as Utilities -- Helpers moved from classes to functions in helpers.php file +- Helpers moved from classes to functions in ```helpers.php``` file +- ```Helpers``` folder renamed as ```Utilities``` +- ```PbHelpers``` class renamed as ```PbUtilities``` - Added singleton for utilities - CRUD permitions refactored - Performance significantly improved - ```crud``` attribute is now optional for models - Added performance flags to CRUD controllers +- Added facades por ```PbUtilities``` class +- Overrides moved to ```Overrides``` folder ## [1.3.6.1] - 2022-02-11 diff --git a/src/Controllers/Config/PbConfigController.php b/src/Controllers/Config/PbConfigController.php index 5f1f3ec..1217501 100644 --- a/src/Controllers/Config/PbConfigController.php +++ b/src/Controllers/Config/PbConfigController.php @@ -16,6 +16,8 @@ use Auth; use DB; use Inertia\Response as InertiaResponse; +use Psr\SimpleCache\InvalidArgumentException; +use ReflectionException; use Session; class PbConfigController extends PbBuilderController @@ -42,6 +44,7 @@ function __construct(Request $request, $crud_perms = false) * * @param string $route * @return InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function create(string $route = 'level'): InertiaResponse|JsonResponse { @@ -70,14 +73,15 @@ public function store(Request $request): Redirector|RedirectResponse|Application * @param null $element * @param bool $multiple * @param string $route - * @return InertiaResponse|JsonResponse + * @return RedirectResponse|InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function edit( int $id, $element = null, bool $multiple = false, string $route = 'level' - ): InertiaResponse|JsonResponse { + ): RedirectResponse|InertiaResponse|JsonResponse { $this->pushRequired(['configkey']); diff --git a/src/Controllers/Navigation/PbNavigationController.php b/src/Controllers/Navigation/PbNavigationController.php index 4ba1ae9..e42fa7e 100644 --- a/src/Controllers/Navigation/PbNavigationController.php +++ b/src/Controllers/Navigation/PbNavigationController.php @@ -4,6 +4,8 @@ use Anibalealvarezs\Projectbuilder\Controllers\PbBuilderController; +use Anibalealvarezs\Projectbuilder\Facades\PbDebugbarFacade as Debug; +use Anibalealvarezs\Projectbuilder\Utilities\PbCache; use App\Http\Requests; use Illuminate\Http\JsonResponse; @@ -14,6 +16,8 @@ use Auth; use DB; use Inertia\Response as InertiaResponse; +use Psr\SimpleCache\InvalidArgumentException; +use ReflectionException; use Session; class PbNavigationController extends PbBuilderController @@ -58,6 +62,7 @@ function __construct(Request $request, $crud_perms = false) * @param bool $multiple * @param string $route * @return InertiaResponse|JsonResponse|RedirectResponse + * @throws ReflectionException|InvalidArgumentException */ public function index( int $page = 1, @@ -69,13 +74,33 @@ public function index( bool $multiple = false, string $route = 'level'): InertiaResponse|JsonResponse|RedirectResponse { + Debug::start('custom_controller', $this->vars->level->names.' crud controller'); + + Debug::measure( + $this->vars->level->names.' crud controller - model list build', + function() use (&$model, $page, $perpage, $orderby, $field, $order) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::withPublicRelations()->orderedByDefault()->get(), + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'getList', + byRoles: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + Debug::stop('custom_controller'); + return parent::index( $page, $perpage, $orderby, $field, $order, - $this->vars->level->modelPath::withPublicRelations()->orderedByDefault()->get() + $model ); } } diff --git a/src/Controllers/PbBuilderController.php b/src/Controllers/PbBuilderController.php index 5ad0e1a..2c7d4e6 100644 --- a/src/Controllers/PbBuilderController.php +++ b/src/Controllers/PbBuilderController.php @@ -2,8 +2,8 @@ namespace Anibalealvarezs\Projectbuilder\Controllers; -use Anibalealvarezs\Projectbuilder\Facades\PbUtilitiesFacade; -use Anibalealvarezs\Projectbuilder\Overrides\Classes\PbDebugbar; +use Anibalealvarezs\Projectbuilder\Facades\PbDebugbarFacade as Debug; +use Anibalealvarezs\Projectbuilder\Utilities\PbCache; use Anibalealvarezs\Projectbuilder\Utilities\PbUtilities; use Anibalealvarezs\Projectbuilder\Utilities\PbShares; use Anibalealvarezs\Projectbuilder\Models\PbCountry; @@ -25,6 +25,8 @@ use Auth; use DB; use Illuminate\Support\Str; +use Psr\SimpleCache\InvalidArgumentException; +use ReflectionException; use Session; use Inertia\Inertia; @@ -47,7 +49,7 @@ function __construct(Request $request, $crud_perms = false) $this->vars->helper->vendor = ($this->vars->helper->vendor ?? resolve($this->vars->helper->class)->vendor); $this->vars->helper->package = ($this->vars->helper->package ?? resolve($this->vars->helper->class)->package); if (!isset($this->vars->helper->keys['level'])) { - $this->vars->helper->keys['level'] = ($this->vars->keys['level'] ?? 'Builder'); + $this->vars->helper->keys['level'] = $this->vars->keys['level'] ?? 'Builder'; } foreach (modelsLevels() as $value) { if (isset($this->vars->helper->keys[$value])) { @@ -65,11 +67,10 @@ function __construct(Request $request, $crud_perms = false) $this->vars->required = $this->getRequired(); $this->vars->request = $request; $this->vars->sortable = isset($this->vars->level->modelPath::$sortable) && $this->vars->level->modelPath::$sortable; + $this->vars->cacheObjects = []; self::$item = $this->vars->level->name; self::$route = $this->vars->level->names; - - PbDebugbar::startMeasure('model_controller', $this->vars->level->names.' crud controller'); } /** @@ -84,6 +85,7 @@ function __construct(Request $request, $crud_perms = false) * @param bool $multiple * @param string $route * @return InertiaResponse|JsonResponse|RedirectResponse + * @throws ReflectionException|InvalidArgumentException */ public function index( int $page = 1, @@ -96,8 +98,7 @@ public function index( string $route = 'level' ): InertiaResponse|JsonResponse|RedirectResponse { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); + Debug::start('builder_controller', 'builder crud controller'); $this->vars->allowed = [ 'create ' . $this->vars->level->names => 'create', @@ -105,9 +106,23 @@ public function index( 'delete ' . $this->vars->level->names => 'delete', ]; - PbDebugbar::measure('builder crud controller - model config load', function() use (&$config) { - $config = ($this->vars->config ?? $this->vars->level->modelPath::getCrudConfig(true)); - }); + Debug::measure( + 'builder crud controller - model config load', + function() use (&$config, $page, $perpage, $orderby, $field, $order) { + $cached = PbCache::run( + closure: fn() => (isset($this->vars->config) && $this->vars->config) ? $this->vars->config : $this->vars->level->modelPath::getCrudConfig(true), + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'getCrudConfig', + pagination: ['page' => $page, 'perpage' => $perpage, 'orderby' => $orderby, 'field' => $field, 'order' => $order], + byRoles: !($this->vars->level->names == 'users'), + byUser: true, + ); + $config = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); if (!isset($config['fields']['actions'])) { $config['fields']['actions'] = [ @@ -115,7 +130,24 @@ public function index( 'delete' => [] ]; } - $config['enabled_actions'] = PbShares::allowed($this->vars->allowed)['allowed']; + + Debug::measure( + 'builder crud controller - model config additional', + function() use (&$config, $page, $perpage, $orderby, $field, $order) { + $cached = PbCache::run( + closure: fn() => PbShares::allowed($this->vars->allowed)['allowed'], + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'sharesAllowed', + pagination: ['page' => $page, 'perpage' => $perpage, 'orderby' => $orderby, 'field' => $field, 'order' => $order], + byRoles: !($this->vars->level->names == 'users'), + byUser: true, + ); + $config['enabled_actions'] = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); if (!isset($config['model'])) { $config['model'] = $this->vars->level->modelPath; @@ -123,21 +155,56 @@ public function index( if (!isset($config['pagination']['page']) || !$config['pagination']['page']) { $config['pagination']['page'] = $page; } - $this->vars->listing = self::buildListingRow($config); - $this->vars->sortable = $this->vars->sortable && app(PbCurrentUser::class)->hasPermissionTo('update '.resolve($this->vars->level->modelPath)->getTable()); - $this->vars->formconfig = ($config['formconfig'] ?? []); - $this->vars->pagination = !$this->vars->sortable ? $config['pagination'] : []; - $this->vars->heading = $config['heading']; - $this->vars->orderby = !$this->vars->sortable && $orderby ? ['field' => $field, 'order' => $order] : []; - PbDebugbar::measure('builder crud controller - model building', function() use (&$arrayElements, $element, $multiple, $page, $perpage, $orderby, $field, $order) { - $arrayElements = $this->buildModelsArray($element, $multiple, null, true, $page, $perpage, $orderby, $field, $order); - }); + Debug::measure( + 'builder crud controller - sorting and pagination', + function() use ($config, $page, $perpage, $orderby, $field, $order) { + $this->vars->sortable = $this->vars->sortable && app(PbCurrentUser::class)->hasPermissionTo('update '.resolve($this->vars->level->modelPath)->getTable()); + $this->vars->formconfig = ($config['formconfig'] ?? []); + $this->vars->pagination = !$this->vars->sortable ? $config['pagination'] : []; + $this->vars->heading = $config['heading']; + $this->vars->orderby = !$this->vars->sortable && $orderby ? ['field' => $field, 'order' => $order] : []; + $cached = PbCache::run( + closure: fn() => self::buildListingRow($config), + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'sortAndPaginate', + pagination: ['page' => $page, 'perpage' => $perpage, 'orderby' => $orderby, 'field' => $field, 'order' => $order], + byRoles: !($this->vars->level->names == 'users'), + byUser: true, + ); + $this->vars->listing = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + Debug::measure( + 'builder crud controller - model list build', + function() use (&$arrayElements, $order, $field, $orderby, $perpage, $page, $multiple, $element) { + $cached = PbCache::run( + closure: function() use (&$arrayElements, $element, $multiple, $page, $perpage, $orderby, $field, $order) { + return $this->buildModelsArray($element, $multiple, null, true, $page, $perpage, $orderby, $field, $order); + }, + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'buildModelsArray', + pagination: ['page' => $page, 'perpage' => $perpage, 'orderby' => $orderby, 'field' => $field, 'order' => $order], + byRoles: !($this->vars->level->names == 'users'), + byUser: true, + ); + $arrayElements = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + Debug::add($arrayElements, 'data'); + Debug::add($this->vars->cacheObjects, 'caches', true); - PbDebugbar::addMessage($arrayElements, 'data'); + Debug::stop('builder_controller'); - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); - return $this->renderResponse($this->buildRouteString($route, 'index'), $arrayElements); + return $this->renderResponse($this->buildRouteString($route, 'index'), (array) $arrayElements); } /** @@ -145,19 +212,34 @@ public function index( * * @param string $route * @return InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function create(string $route = 'level'): InertiaResponse|JsonResponse { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); + Debug::start('builder_controller', 'builder crud controller'); + + Debug::measure( + 'builder crud controller - model config load', + function() use (&$config) { + $cached = PbCache::run( + closure: fn() => (isset($this->vars->config) && $this->vars->config) ? $this->vars->config : $this->vars->level->modelPath::getCrudConfig(true), + package: $this->vars->helper->package, + class: __CLASS__, + function: 'create', + model: $this->vars->level->names, + modelFunction: 'getCrudConfig', + byRoles: true, + ); + $this->vars->formconfig = $cached['data']['formconfig']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); - PbDebugbar::measure('builder crud controller - model config load', function() { - $this->vars->formconfig = $this->vars->level->modelPath::getCrudConfig(true)['formconfig']; - }); + Debug::add($this->vars->formconfig, 'formconfig'); + Debug::add($this->vars->cacheObjects, 'caches', true); - PbDebugbar::addMessage($this->vars->formconfig, 'formconfig'); + Debug::stop('builder_controller'); - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); return $this->renderResponse($this->buildRouteString($route, 'create')); } @@ -169,9 +251,6 @@ public function create(string $route = 'level'): InertiaResponse|JsonResponse */ public function store(Request $request): Redirector|RedirectResponse|Application|null { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); - // Validation if ($failed = $this->validateRequest($this->vars->validationRules, $request)) { return $failed; @@ -180,8 +259,12 @@ public function store(Request $request): Redirector|RedirectResponse|Application // Process try { // Add requests - $model = $this->processModelRequests($this->vars->validationRules, $request, $this->vars->replacers, - (new $this->vars->level->modelPath())->setLocale(app()->getLocale())); + $model = $this->processModelRequests( + validationRules: $this->vars->validationRules, + request: $request, + replacers: $this->vars->replacers, + model: resolve($this->vars->level->modelPath)->setLocale(app()->getLocale()) + ); // Check if module relation exists if ($model->hasRelation('module') && ($request->input('module') > 0) && $module = PbModule::find($request->input('module'))) { $model->module()->associate($module); @@ -191,7 +274,6 @@ public function store(Request $request): Redirector|RedirectResponse|Application return $this->redirectResponseCRUDFail($request, 'create', "Error saving {$this->vars->level->name}"); } - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); return $this->redirectResponseCRUDSuccess($request, 'create'); } catch (Exception $e) { return $this->redirectResponseCRUDFail($request, 'create', $e->getMessage()); @@ -206,6 +288,7 @@ public function store(Request $request): Redirector|RedirectResponse|Application * @param bool $multiple * @param string $route * @return Application|RedirectResponse|Redirector|InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function show( int $id, @@ -214,22 +297,68 @@ public function show( string $route = 'level' ): Application|RedirectResponse|Redirector|InertiaResponse|JsonResponse { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); - - $currentModel = $this->vars->level->modelPath::find($id); + Debug::start('builder_controller', 'builder crud controller'); + + if (!$element) { + Debug::measure( + 'builder crud controller - model find', + function() use (&$element, $id) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::find($id), + package: $this->vars->helper->package, + class: __CLASS__, + function: 'show', + model: $this->vars->level->name, + modelFunction: 'find', + modelId: $id, + byRoles: true, + ); + $element = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); - if (!$model = $this->buildModelsArray($element, $multiple, $id)) { - return $this->redirectResponseCRUDFail(request(), 'show', "Error finding {$this->vars->level->name}"); + if (!$element) { + return $this->redirectResponseCRUDFail(request(), 'show', "Error finding {$this->vars->level->name}"); + } } - if ($this->isUnreadableModel($currentModel)) { + + Debug::start('permissions_check', 'permissions check'); + if ($this->isUnreadableModel($element)) { return $this->redirectResponseCRUDFail(request(), 'show', "This {$this->vars->level->name} cannot be shown"); } - if (!$currentModel->isViewableBy(Auth::user()->id)) { + if (!$element->isViewableBy(Auth::user()->id)) { return $this->redirectResponseCRUDFail(request(), 'show', "You don't have permission to view this {$this->vars->level->name}"); } + Debug::stop('permissions_check'); + + Debug::measure( + 'builder crud controller - model data build', + function() use (&$model, $element, $multiple, $id) { + $cached = PbCache::run( + closure: fn() => $this->buildModelsArray($element, $multiple, $id), + package: $this->vars->helper->package, + class: __CLASS__, + function: 'show', + model: $this->vars->level->name, + modelFunction: 'buildModelsArray', + modelId: $id, + byRoles: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + Debug::add($model, 'data'); + Debug::add($this->vars->cacheObjects, 'caches', true); + + if (!$model) { + return $this->redirectResponseCRUDFail(request(), 'show', "Error finding {$this->vars->level->name}"); + } + + Debug::stop('builder_controller'); - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); return $this->renderResponse($this->buildRouteString($route, 'show'), $model); } @@ -240,37 +369,84 @@ public function show( * @param null $element * @param bool $multiple * @param string $route - * @return InertiaResponse|JsonResponse + * @return RedirectResponse|InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function edit( int $id, $element = null, bool $multiple = false, string $route = 'level' - ): InertiaResponse|JsonResponse { + ): RedirectResponse|InertiaResponse|JsonResponse { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); + Debug::start('builder_controller', 'builder crud controller'); - PbDebugbar::measure('builder crud controller - model config load', function() { + Debug::measure('builder crud controller - model config load', function() { $this->vars->formconfig = $this->vars->level->modelPath::getCrudConfig(true)['formconfig']; }); - PbDebugbar::addMessage($this->vars->formconfig, 'formconfig'); - - $currentModel = $this->vars->level->modelPath::find($id); + Debug::add($this->vars->formconfig, 'formconfig'); + + if (!$element) { + Debug::measure( + 'builder crud controller - model find', + function() use (&$element, $id) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::find($id), + package: $this->vars->helper->package, + class: __CLASS__, + function: 'edit', + model: $this->vars->level->name, + modelFunction: 'find', + modelId: $id, + byRoles: true, + ); + $element = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); - if (!$model = $this->buildModelsArray($element, $multiple, $id)) { - return $this->redirectResponseCRUDFail(request(), 'edit', "Error finding {$this->vars->level->name}"); + if (!$element) { + return $this->redirectResponseCRUDFail(request(), 'edit', "Error finding {$this->vars->level->name}"); + } } - if ($this->isUnreadableModel($currentModel)) { + + Debug::start('permissions_check', 'permissions check'); + if ($this->isUnreadableModel($element)) { return $this->redirectResponseCRUDFail(request(), 'edit', "This {$this->vars->level->name} cannot be modified"); } - if (!$currentModel->isEditableBy(Auth::user()->id)) { + if (!$element->isEditableBy(Auth::user()->id)) { return $this->redirectResponseCRUDFail(request(), 'edit', "You don't have permission to edit this {$this->vars->level->name}"); } + Debug::stop('permissions_check'); + + Debug::measure( + 'builder crud controller - model data build', + function() use (&$model, $element, $multiple, $id) { + $cached = PbCache::run( + closure: fn() => $this->buildModelsArray($element, $multiple, $id), + package: $this->vars->helper->package, + class: __CLASS__, + function: 'edit', + model: $this->vars->level->name, + modelFunction: 'buildModelsArray', + modelId: $id, + byRoles: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + Debug::add($model, 'data'); + Debug::add($this->vars->cacheObjects, 'caches', true); + + if (!$model) { + return $this->redirectResponseCRUDFail(request(), 'edit', "Error finding {$this->vars->level->name}"); + } + + Debug::stop('builder_controller'); - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); return $this->renderResponse($this->buildRouteString($route, 'edit'), $model); } @@ -283,8 +459,8 @@ public function edit( */ public function update(Request $request, int $id): Redirector|RedirectResponse|Application|null { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); + Debug::stop('model_controller'); + Debug::start('builder_controller', 'builder crud controller'); // Validation if ($failed = $this->validateRequest($this->vars->validationRules, $request)) { @@ -305,7 +481,11 @@ public function update(Request $request, int $id): Redirector|RedirectResponse|A } $model->setLocale(app()->getLocale()); // Build requests - $requests = $this->processModelRequests($this->vars->validationRules, $request, $this->vars->replacers); + $requests = $this->processModelRequests( + validationRules: $this->vars->validationRules, + request: $request, + replacers: $this->vars->replacers, + ); // Check if module relation exists if ($model->hasRelation('module') && ($request->input('module') > 0) && $module = PbModule::find($request->input('module'))) { $model->module()->associate($module); @@ -315,7 +495,6 @@ public function update(Request $request, int $id): Redirector|RedirectResponse|A return $this->redirectResponseCRUDFail($request, 'update', "Error updating {$this->vars->level->name}"); } - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); return $this->redirectResponseCRUDSuccess($request, 'update'); } catch (Exception $e) { return $this->redirectResponseCRUDFail($request, 'update', $e->getMessage()); @@ -331,8 +510,8 @@ public function update(Request $request, int $id): Redirector|RedirectResponse|A */ public function destroy(Request $request, int $id): Redirector|RedirectResponse|Application { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); + Debug::stop('model_controller'); + Debug::start('builder_controller', 'builder crud controller'); if (!$model = $this->vars->level->modelPath::find($id)) { return $this->redirectResponseCRUDFail($request, 'delete', "Error finding {$this->vars->level->name}"); @@ -350,7 +529,6 @@ public function destroy(Request $request, int $id): Redirector|RedirectResponse| return $this->redirectResponseCRUDFail($request, 'delete', "Error deleting {$this->vars->level->name}"); } - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); return $this->redirectResponseCRUDSuccess($request, 'delete'); } catch (Exception $e) { return $this->redirectResponseCRUDFail($request, 'delete', $e->getMessage()); @@ -366,8 +544,8 @@ public function destroy(Request $request, int $id): Redirector|RedirectResponse| */ public function sort(Request $request, int $id): Redirector|RedirectResponse|Application|null { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); + Debug::stop('model_controller'); + Debug::start('builder_controller', 'builder crud controller'); // Validation if ($failed = $this->validateRequest(['sortlist' => ['required']], $request)) { @@ -398,7 +576,6 @@ public function sort(Request $request, int $id): Redirector|RedirectResponse|App $n++; } - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); return $this->redirectResponseCRUDSuccess($request, 'sort'); } catch (Exception $e) { return $this->redirectResponseCRUDFail($request, @@ -415,10 +592,10 @@ public function sort(Request $request, int $id): Redirector|RedirectResponse|App */ public function enable(Request $request, int $id): Redirector|RedirectResponse|Application|null { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); + Debug::stop('model_controller'); + Debug::start('builder_controller', 'builder crud controller'); - if (isset($this->vars->level->modelPath::$enableable) && (new $this->vars->level->modelPath)->isEditableBy(Auth::user()->id)) { + if (isset($this->vars->level->modelPath::$enableable) && resolve($this->vars->level->modelPath)->isEditableBy(Auth::user()->id)) { $model = $this->vars->level->modelPath::find($id); if ($this->isUnmodifiableModel($model)) { return $this->redirectResponseCRUDFail($request, 'enable', "This {$this->vars->level->name} cannot be modified"); @@ -428,7 +605,6 @@ public function enable(Request $request, int $id): Redirector|RedirectResponse|A } try { if ($model->enable()) { - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); return $this->redirectResponseCRUDSuccess($request, 'enable'); } return $this->redirectResponseCRUDFail($request, 'enable', @@ -452,10 +628,10 @@ public function enable(Request $request, int $id): Redirector|RedirectResponse|A */ public function disable(Request $request, int $id): Redirector|RedirectResponse|Application|null { - PbDebugbar::stopMeasure('model_controller'); - PbDebugbar::startMeasure('builder_controller', 'builder crud controller'); + Debug::stop('model_controller'); + Debug::start('builder_controller', 'builder crud controller'); - if (isset($this->vars->level->modelPath::$enableable) && (new $this->vars->level->modelPath)->isEditableBy(Auth::user()->id)) { + if (isset($this->vars->level->modelPath::$enableable) && resolve($this->vars->level->modelPath)->isEditableBy(Auth::user()->id)) { $model = $this->vars->level->modelPath::find($id); if ($this->isUnmodifiableModel($model)) { return $this->redirectResponseCRUDFail($request, 'disable', "This {$this->vars->level->name} cannot be modified"); @@ -465,7 +641,6 @@ public function disable(Request $request, int $id): Redirector|RedirectResponse| } try { if ($model->disable()) { - PbDebugbar::startMeasure('builder_controller_response_building', 'builder crud controller - response building'); return $this->redirectResponseCRUDSuccess($request, 'disable'); } return $this->redirectResponseCRUDFail($request, 'disable', @@ -515,9 +690,19 @@ protected function buildModelsArray( $arrayElements[($plural ? $this->vars->level->prefixNames : $this->vars->level->prefixName)] = $element; } } else { - $arrayElements[ - ($plural ? $this->vars->level->prefixNames : $this->vars->level->prefixName)] = - ($id ? $this->vars->level->modelPath::find($id) : $this->buildPaginatedAndOrderedModel(null, $page, $perpage, $orderby, $field, $order)); + $arrayElements[($plural ? + $this->vars->level->prefixNames : + $this->vars->level->prefixName + )] = ($id ? + $this->vars->level->modelPath::find($id) : + $this->buildPaginatedAndOrderedModel( + page: $page, + perpage: $perpage, + orderby: $orderby, + field: $field, + order: $order, + ) + ); } return $arrayElements; @@ -542,12 +727,12 @@ protected function shareVars() ...['defaults' => $this->getDefaults()], ...['listing' => $this->vars->listing], ...['formconfig' => $this->vars->formconfig], - ...['pagination' => $this->vars->pagination ?? ['location' => 'none']], - ...['heading' => $this->vars->heading ?? ['location' => 'none']], + ...['pagination' => (isset($this->vars->pagination) && $this->vars->pagination) ? $this->vars->pagination : ['location' => 'none']], + ...['heading' => (isset($this->vars->heading) && $this->vars->heading) ? $this->vars->heading : ['location' => 'none']], ...['orderby' => $this->vars->orderby ?? []], ]; Inertia::share('shared', $shared); - PbDebugbar::addMessage($shared, 'shared'); + Debug::add($shared, 'shared'); } /** @@ -593,7 +778,12 @@ protected function getRequired() * @param null $model * @return array|object */ - protected function processModelRequests(array $validationRules, Request $request, array $replacers, $model = null): array|object + protected function processModelRequests( + array $validationRules, + Request $request, + array $replacers, + $model = null + ): array|object { $keys = []; foreach ($validationRules as $vrKey => $vr) { @@ -637,15 +827,16 @@ protected function renderResponse($view, array $elements = [], bool $nullable = { // If not API if (!isApi($this->vars->request)) { + Debug::start('builder_controller_response_building', 'builder crud controller - response building'); - PbDebugbar::measure('builder crud controller - share building', function() { + Debug::measure('builder crud controller - share building', function() { $this->shareVars(); }); Inertia::setRootView($this->vars->inertiaRoot); - PbDebugbar::stopMeasure('builder_controller'); - PbDebugbar::stopMeasure('builder_controller_response_building'); + Debug::stop('builder_controller'); + Debug::stop('builder_controller_response_building'); return Inertia::render($view, $elements); } @@ -785,9 +976,11 @@ public function arraytify($array) */ public function isUndeletableModel($model): bool { - foreach($model->undeletableModels as $key => $value) { - if (in_array($model->{$key}, $value)) { - return true; + if (isset($model->undeletableModels)) { + foreach($model->undeletableModels as $key => $value) { + if (in_array($model->{$key}, $value)) { + return true; + } } } return false; @@ -801,9 +994,11 @@ public function isUndeletableModel($model): bool */ public function isUnmodifiableModel($model): bool { - foreach($model->unmodifiableModels as $key => $value) { - if (in_array($model->{$key}, $value)) { - return true; + if (isset($model->unmodifiableModels)) { + foreach($model->unmodifiableModels as $key => $value) { + if (in_array($model->{$key}, $value)) { + return true; + } } } return false; @@ -817,9 +1012,11 @@ public function isUnmodifiableModel($model): bool */ public function isUnreadableModel($model): bool { - foreach($model->unreadableModels as $key => $value) { - if (in_array($model->{$key}, $value)) { - return true; + if (isset($model->unreadableModels)) { + foreach($model->unreadableModels as $key => $value) { + if (in_array($model->{$key}, $value)) { + return true; + } } } return false; @@ -833,9 +1030,11 @@ public function isUnreadableModel($model): bool */ public function isUnconfigurableModel($model): bool { - foreach($model->unconfigurableModels as $key => $value) { - if (in_array($model->{$key}, $value)) { - return true; + if (isset($model->unconfigurableModels)) { + foreach($model->unconfigurableModels as $key => $value) { + if (in_array($model->{$key}, $value)) { + return true; + } } } return false; diff --git a/src/Controllers/Permission/PbPermissionController.php b/src/Controllers/Permission/PbPermissionController.php index 979ec77..cbdb5e3 100644 --- a/src/Controllers/Permission/PbPermissionController.php +++ b/src/Controllers/Permission/PbPermissionController.php @@ -7,6 +7,8 @@ use Anibalealvarezs\Projectbuilder\Models\PbModule; use Anibalealvarezs\Projectbuilder\Models\PbRole; +use Anibalealvarezs\Projectbuilder\Facades\PbDebugbarFacade as Debug; +use Anibalealvarezs\Projectbuilder\Utilities\PbCache; use App\Http\Requests; use Illuminate\Contracts\Foundation\Application; @@ -20,6 +22,8 @@ use Inertia\Response as InertiaResponse; +use Psr\SimpleCache\InvalidArgumentException; +use ReflectionException; use Session; class PbPermissionController extends PbBuilderController @@ -55,6 +59,7 @@ public function __construct(Request $request, $crud_perms = false) * @param bool $multiple * @param string $route * @return InertiaResponse|JsonResponse|RedirectResponse + * @throws ReflectionException|InvalidArgumentException */ public function index( int $page = 1, @@ -64,35 +69,77 @@ public function index( string $order = 'asc', $element = null, bool $multiple = false, - string $route = 'level' - ): InertiaResponse|JsonResponse|RedirectResponse { + string $route = 'level'): InertiaResponse|JsonResponse|RedirectResponse + { + Debug::start('custom_controller', $this->vars->level->names.' crud controller'); - $me = app(PbCurrentUser::class); - $toExclude = ['crud super-admin']; - if (!$me->hasRole('super-admin')) { - $toExclude = [ - ...$toExclude, - ...[ - 'admin roles permissions', - 'manage app', - 'crud super-admin', - 'config builder', - 'developer options', - 'read loggers', - 'delete loggers', - 'config loggers', - 'api access', - ] - ]; - if (!$me->hasRole('admin')) { - $toExclude = [ - ...$toExclude, - ...['login', 'create users', 'update users', 'delete users'] - ]; + Debug::measure( + $this->vars->level->names.' crud controller - model config load', + function() use ($page, $perpage, $orderby, $field, $order) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::getCrudConfig(true), + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'getCrudConfig', + pagination: ['page' => $page, 'perpage' => $perpage, 'orderby' => $orderby, 'field' => $field, 'order' => $order], + byRoles: true, + ); + $this->vars->config = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; } - } + ); - $this->vars->config = $this->vars->level->modelPath::getCrudConfig(true); + Debug::measure( + $this->vars->level->names.' crud controller - model list build', + function() use (&$model, $page, $perpage, $orderby, $field, $order) { + $cached = PbCache::run( + closure: function() use ($order, $field, $orderby, $perpage, $page) { + $me = app(PbCurrentUser::class); + $toExclude = ['crud super-admin']; + if (!$me->hasRole('super-admin')) { + $toExclude = [ + ...$toExclude, + ...[ + 'admin roles permissions', + 'manage app', + 'crud super-admin', + 'config builder', + 'developer options', + 'read loggers', + 'delete loggers', + 'config loggers', + 'api access', + ] + ]; + if (!$me->hasRole('admin')) { + $toExclude = [ + ...$toExclude, + ...['login', 'create users', 'update users', 'delete users'] + ]; + } + } + return $this->buildPaginatedAndOrderedModel( + query: $this->vars->level->modelPath::whereNotIn('name', $toExclude)->withPublicRelations(), + page: $page, + perpage: $perpage, + orderby: $orderby, + field: $field, + order: $order, + ); + }, + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'getList', + byRoles: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + Debug::stop('custom_controller'); return parent::index( $page, @@ -100,14 +147,7 @@ public function index( $orderby, $field, $order, - $this->buildPaginatedAndOrderedModel( - $this->vars->level->modelPath::whereNotIn('name', $toExclude)->withPublicRelations(), - $page, - $perpage, - $orderby, - $field, - $order - ) + $model ); } @@ -129,9 +169,14 @@ public function store(Request $request): Redirector|RedirectResponse|Application // Process try { // Build model - $model = (new $this->vars->level->modelPath())->setLocale(app()->getLocale()); + $model = resolve($this->vars->level->modelPath)->setLocale(app()->getLocale()); // Add requests - $model = $this->processModelRequests($this->vars->validationRules, $request, $this->vars->replacers, $model); + $model = $this->processModelRequests( + validationRules: $this->vars->validationRules, + request: $request, + replacers: $this->vars->replacers, + model: $model + ); // Add additional fields values $model->guard_name = 'admin'; // Check if module relation exists @@ -165,6 +210,7 @@ public function store(Request $request): Redirector|RedirectResponse|Application * @param bool $multiple * @param string $route * @return Application|RedirectResponse|Redirector|InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function show( int $id, @@ -183,16 +229,39 @@ public function show( * @param null $element * @param bool $multiple * @param string $route - * @return InertiaResponse|JsonResponse + * @return RedirectResponse|InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException|InvalidArgumentException */ public function edit( int $id, $element = null, bool $multiple = false, string $route = 'level' - ): InertiaResponse|JsonResponse { + ): RedirectResponse|InertiaResponse|JsonResponse { + + Debug::measure( + $this->vars->level->names.' crud controller - model find', + function() use (&$model, $id) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::withPublicRelations()->find($id), + package: $this->vars->helper->package, + class: __CLASS__, + function: 'edit', + model: $this->vars->level->name, + modelFunction: 'find', + modelId: $id, + byRoles: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); - return parent::edit($id, $this->vars->level->modelPath::withPublicRelations()->findOrFail($id)); + if (!$model) { + return $this->redirectResponseCRUDFail(request(), 'edit', "Error finding {$this->vars->level->name}"); + } + + return parent::edit($id, $model); } /** @@ -224,7 +293,11 @@ public function update(Request $request, int $id): Redirector|RedirectResponse|A // Push Unmodifiable Permissions $model = $this->pushUnmodifiablePermissions($model); // Build requests - $requests = $this->processModelRequests($this->vars->validationRules, $request, $this->vars->replacers); + $requests = $this->processModelRequests( + validationRules: $this->vars->validationRules, + request: $request, + replacers: $this->vars->replacers, + ); // Check if module relation exists if ($model->hasRelation('module') && ($request->input('module') > 0) && $module = PbModule::find($request->input('module'))) { $model->module()->associate($module); diff --git a/src/Controllers/Role/PbRoleController.php b/src/Controllers/Role/PbRoleController.php index 302b727..f893ae2 100644 --- a/src/Controllers/Role/PbRoleController.php +++ b/src/Controllers/Role/PbRoleController.php @@ -6,6 +6,8 @@ use Anibalealvarezs\Projectbuilder\Models\PbCurrentUser; use Anibalealvarezs\Projectbuilder\Models\PbPermission; +use Anibalealvarezs\Projectbuilder\Facades\PbDebugbarFacade as Debug; +use Anibalealvarezs\Projectbuilder\Utilities\PbCache; use App\Http\Requests; use Illuminate\Contracts\Foundation\Application; @@ -20,6 +22,8 @@ use Inertia\Response as InertiaResponse; +use Psr\SimpleCache\InvalidArgumentException; +use ReflectionException; use Session; class PbRoleController extends PbBuilderController @@ -58,6 +62,7 @@ public function __construct(Request $request, $crud_perms = false) * @param bool $multiple * @param string $route * @return InertiaResponse|JsonResponse|RedirectResponse + * @throws ReflectionException|InvalidArgumentException */ public function index( int $page = 1, @@ -69,15 +74,57 @@ public function index( bool $multiple = false, string $route = 'level'): InertiaResponse|JsonResponse|RedirectResponse { - $this->pushRequired(['name']); - - $this->vars->config = $this->vars->level->modelPath::getCrudConfig(true); + Debug::start('custom_controller', $this->vars->level->names.' crud controller'); + Debug::measure($this->vars->level->names.' crud controller - push required fields', fn() => $this->pushRequired(['name'])); + + Debug::measure( + $this->vars->level->names.' crud controller - model config load', + function() use ($page, $perpage, $orderby, $field, $order) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::getCrudConfig(true), + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'getCrudConfig', + pagination: ['page' => $page, 'perpage' => $perpage, 'orderby' => $orderby, 'field' => $field, 'order' => $order], + byRoles: true, + ); + $this->vars->config = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); - $query = $this->vars->level->modelPath::withPublicRelations()->whereNotIn('name', ['super-admin', 'developer', 'api-user']); + Debug::measure( + $this->vars->level->names.' crud controller - model list build', + function() use (&$model, $page, $perpage, $orderby, $field, $order) { + $cached = PbCache::run( + closure: function() use ($order, $field, $orderby, $perpage, $page) { + $query = $this->vars->level->modelPath::withPublicRelations()->whereNotIn('name', ['super-admin', 'developer', 'api-user']); + if (!app(PbCurrentUser::class)->hasRole('super-admin')) { + $query->whereNotIn('name', ['admin']); + } + return $this->buildPaginatedAndOrderedModel( + query: $query, + page: $page, + perpage: $perpage, + orderby: $orderby, + field: $field, + order: $order, + ); + }, + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'getList', + pagination: ['page' => $page, 'perpage' => $perpage, 'orderby' => $orderby, 'field' => $field, 'order' => $order], + byRoles: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); - if (!app(PbCurrentUser::class)->hasRole('super-admin')) { - $query->whereNotIn('name', ['admin']); - } + Debug::stop('custom_controller'); return parent::index( $page, @@ -85,14 +132,7 @@ public function index( $orderby, $field, $order, - $this->buildPaginatedAndOrderedModel( - $query, - $page, - $perpage, - $orderby, - $field, - $order - ) + $model, ); } @@ -101,6 +141,7 @@ public function index( * * @param string $route * @return InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function create(string $route = 'level'): InertiaResponse|JsonResponse { @@ -131,9 +172,14 @@ public function store(Request $request): Redirector|RedirectResponse|Application // Process try { // Build model - $model = (new $this->vars->level->modelPath())->setLocale(app()->getLocale()); + $model = resolve($this->vars->level->modelPath)->setLocale(app()->getLocale()); // Add requests - $model = $this->processModelRequests($this->vars->validationRules, $request, $this->vars->replacers, $model); + $model = $this->processModelRequests( + validationRules: $this->vars->validationRules, + request: $request, + replacers: $this->vars->replacers, + model: $model + ); // Add additional fields values $model->guard_name = 'admin'; // Model save @@ -162,6 +208,7 @@ public function store(Request $request): Redirector|RedirectResponse|Application * @param bool $multiple * @param string $route * @return Application|RedirectResponse|Redirector|InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function show(int $id, $element = null, bool $multiple = false, string $route = 'level'): Application|RedirectResponse|Redirector|InertiaResponse|JsonResponse { @@ -175,13 +222,36 @@ public function show(int $id, $element = null, bool $multiple = false, string $r * @param null $element * @param bool $multiple * @param string $route - * @return InertiaResponse|JsonResponse + * @return RedirectResponse|InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ - public function edit(int $id, $element = null, bool $multiple = false, string $route = 'level'): InertiaResponse|JsonResponse + public function edit(int $id, $element = null, bool $multiple = false, string $route = 'level'): RedirectResponse|InertiaResponse|JsonResponse { $this->pushRequired(['name']); - return parent::edit($id, $this->vars->level->modelPath::withPublicRelations()->whereNotIn('name', ['super-admin', 'developer', 'api-user'])->findOrFail($id)); + Debug::measure( + $this->vars->level->names.' crud controller - model find', + function() use (&$model, $id) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::withPublicRelations()->whereNotIn('name', ['super-admin', 'developer', 'api-user'])->find($id), + package: $this->vars->helper->package, + class: __CLASS__, + function: 'edit', + model: $this->vars->level->name, + modelFunction: 'find', + modelId: $id, + byRoles: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + if (!$model) { + return $this->redirectResponseCRUDFail(request(), 'edit', "Error finding {$this->vars->level->name}"); + } + + return parent::edit($id, $model); } /** @@ -220,7 +290,11 @@ public function update(Request $request, int $id): Redirector|RedirectResponse|A } $model->setLocale(app()->getLocale()); // Build requests - $requests = $this->processModelRequests($this->vars->validationRules, $request, $this->vars->replacers); + $requests = $this->processModelRequests( + validationRules: $this->vars->validationRules, + request: $request, + replacers: $this->vars->replacers, + ); // Model update if (!$model->update($requests)) { return $this->redirectResponseCRUDFail($request, 'update', "Error updating {$this->vars->level->name}"); diff --git a/src/Controllers/User/PbUserController.php b/src/Controllers/User/PbUserController.php index ccc4ddd..69a62e0 100644 --- a/src/Controllers/User/PbUserController.php +++ b/src/Controllers/User/PbUserController.php @@ -5,7 +5,8 @@ use Anibalealvarezs\Projectbuilder\Controllers\PbBuilderController; use Anibalealvarezs\Projectbuilder\Models\PbCurrentUser; -use Anibalealvarezs\Projectbuilder\Overrides\Classes\PbDebugbar; +use Anibalealvarezs\Projectbuilder\Facades\PbDebugbarFacade as Debug; +use Anibalealvarezs\Projectbuilder\Utilities\PbCache; use App\Http\Requests; use App\Models\Team; @@ -18,6 +19,8 @@ use Auth; use DB; +use Psr\SimpleCache\InvalidArgumentException; +use ReflectionException; use Session; use Inertia\Response as InertiaResponse; @@ -62,6 +65,7 @@ function __construct(Request $request, $crud_perms = false) * @param bool $multiple * @param string $route * @return InertiaResponse|JsonResponse|RedirectResponse + * @throws ReflectionException|InvalidArgumentException */ public function index( int $page = 1, @@ -71,24 +75,57 @@ public function index( string $order = 'asc', $element = null, bool $multiple = false, - string $route = 'level' - ): InertiaResponse|JsonResponse|RedirectResponse + string $route = 'level'): InertiaResponse|JsonResponse|RedirectResponse { - $this->pushRequired(['roles', 'email']); + Debug::start('custom_controller', $this->vars->level->names.' crud controller'); + Debug::measure($this->vars->level->names.' crud controller - push required fields', fn() => $this->pushRequired(['roles', 'email'])); $this->vars->shares[] = 'me'; - $this->vars->config = $this->vars->level->modelPath::getCrudConfig(true); - $model = $this->buildPaginatedAndOrderedModel( - $this->vars->level->modelPath::withPublicRelations()->removeAdmins(), - $page, - $perpage, - $orderby, - $field, - $order, - ['name' => 'asc', 'email' => 'asc'] + Debug::measure( + $this->vars->level->names.' crud controller - model config load', + function() use ($page, $perpage, $orderby, $field, $order) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::getCrudConfig(true), + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'getCrudConfig', + pagination: ['page' => $page, 'perpage' => $perpage, 'orderby' => $orderby, 'field' => $field, 'order' => $order], + byUser: true, + ); + $this->vars->config = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } ); + Debug::measure( + $this->vars->level->names.' crud controller - model list build', + function() use (&$model, $page, $perpage, $orderby, $field, $order) { + $cached = PbCache::run( + closure: fn() => $this->buildPaginatedAndOrderedModel( + query: $this->vars->level->modelPath::withPublicRelations()->removeAdmins(), + page: $page, + perpage: $perpage, + orderby: $orderby, + field: $field, + order: $order, + defaultOrder: ['name' => 'asc', 'email' => 'asc'] + ), + package: $this->vars->helper->package, + class: __CLASS__, + model: $this->vars->level->names, + modelFunction: 'getList', + pagination: ['page' => $page, 'perpage' => $perpage, 'orderby' => $orderby, 'field' => $field, 'order' => $order], + byUser: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + Debug::stop('custom_controller'); + return parent::index( $page, $perpage, @@ -104,6 +141,7 @@ public function index( * * @param string $route * @return InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function create(string $route = 'level'): InertiaResponse|JsonResponse { @@ -113,7 +151,7 @@ public function create(string $route = 'level'): InertiaResponse|JsonResponse $this->pushRequired(['roles', 'password', 'email']); - return $this->renderResponse($this->buildRouteString($route, 'create')); + return parent::create($route); } /** @@ -141,9 +179,14 @@ public function store(Request $request): Redirector|RedirectResponse|Application // Process try { // Build model - $model = (new $this->vars->level->modelPath())->setLocale(app()->getLocale()); + $model = resolve($this->vars->level->modelPath)->setLocale(app()->getLocale()); // Add requests - $model = $this->processModelRequests($this->vars->validationRules, $request, $this->vars->replacers, $model); + $model = $this->processModelRequests( + validationRules: $this->vars->validationRules, + request: $request, + replacers: $this->vars->replacers, + model: $model + ); $model->language_id = $lang; $model->country_id = $country; // Model save @@ -195,6 +238,7 @@ public function store(Request $request): Redirector|RedirectResponse|Application * @param bool $multiple * @param string $route * @return Application|RedirectResponse|Redirector|InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ public function show(int $id, $element = null, bool $multiple = false, string $route = 'level'): Application|RedirectResponse|Redirector|InertiaResponse|JsonResponse { @@ -202,7 +246,29 @@ public function show(int $id, $element = null, bool $multiple = false, string $r return redirect(DIRECTORY_SEPARATOR.$this->vars->level->name.'/profile'); } - return parent::show($id, $this->vars->level->modelPath::withPublicRelations()->find($id)); + Debug::measure( + $this->vars->level->names.' crud controller - model find', + function() use (&$model, $id) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::withPublicRelations()->find($id), + package: $this->vars->helper->package, + class: __CLASS__, + function: 'show', + model: $this->vars->level->name, + modelFunction: 'find', + modelId: $id, + byRoles: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + if (!$model) { + return $this->redirectResponseCRUDFail(request(), 'show', "Error finding {$this->vars->level->name}"); + } + + return parent::show($id, $model); } /** @@ -212,9 +278,10 @@ public function show(int $id, $element = null, bool $multiple = false, string $r * @param null $element * @param bool $multiple * @param string $route - * @return InertiaResponse|JsonResponse + * @return RedirectResponse|InertiaResponse|JsonResponse + * @throws ReflectionException|InvalidArgumentException */ - public function edit(int $id, $element = null, bool $multiple = false, string $route = 'level'): InertiaResponse|JsonResponse + public function edit(int $id, $element = null, bool $multiple = false, string $route = 'level'): RedirectResponse|InertiaResponse|JsonResponse { if ((Auth::user()->id == $id) && !isApi($this->vars->request)) { return redirect(DIRECTORY_SEPARATOR.$this->vars->level->name.'/profile'); @@ -222,7 +289,29 @@ public function edit(int $id, $element = null, bool $multiple = false, string $r $this->pushRequired(['roles', 'email']); - return parent::edit($id, $this->vars->level->modelPath::withPublicRelations()->find($id)); + Debug::measure( + $this->vars->level->names.' crud controller - model find', + function() use (&$model, $id) { + $cached = PbCache::run( + closure: fn() => $this->vars->level->modelPath::withPublicRelations()->find($id), + package: $this->vars->helper->package, + class: __CLASS__, + function: 'edit', + model: $this->vars->level->name, + modelFunction: 'find', + modelId: $id, + byRoles: true, + ); + $model = $cached['data']; + $this->vars->cacheObjects[] = $cached['index']; + } + ); + + if (!$model) { + return $this->redirectResponseCRUDFail(request(), 'edit', "Error finding {$this->vars->level->name}"); + } + + return parent::edit($id, $model); } /** @@ -263,7 +352,11 @@ public function update(Request $request, int $id): Redirector|RedirectResponse|A } $model->setLocale(app()->getLocale()); // Build requests - $requests = $this->processModelRequests($this->vars->validationRules, $request, $this->vars->replacers); + $requests = $this->processModelRequests( + validationRules: $this->vars->validationRules, + request: $request, + replacers: $this->vars->replacers, + ); if ($password != "") { $requests['password'] = $password; } diff --git a/src/Database/Seeders/PbConfigSeeder.php b/src/Database/Seeders/PbConfigSeeder.php index ac53398..3333286 100644 --- a/src/Database/Seeders/PbConfigSeeder.php +++ b/src/Database/Seeders/PbConfigSeeder.php @@ -31,6 +31,7 @@ public function run() ['configkey' => '_DISABLE_LOGIN_', 'configvalue' => false, 'name' => json_encode(['en' => 'Disable login form', 'es' => 'Deshabilitar formulario de ingreso']), 'description' => json_encode(['en' => 'Disable the login form', 'es' => 'Deshabilitar el formulario de ingreso']), 'module_id' => $moduleConfig->id], ['configkey' => '_DISABLE_REGISTER_', 'configvalue' => false, 'name' => json_encode(['en' => 'Disable registration form', 'es' => 'Deshabilitar formulario de registro']), 'description' => json_encode(['en' => 'Disable the registration form', 'es' => 'Deshabilitar el formulario de registro']), 'module_id' => $moduleConfig->id], ['configkey' => '_DEFAULT_TABLE_SIZE_', 'configvalue' => false, 'name' => json_encode(['en' => 'Default table size', 'es' => 'Tamaño de la tabla por defecto']), 'description' => json_encode(['en' => 'Number of rows to be shown in tables per page', 'es' => 'Número de registros a mostrar en las tablas por defecto']), 'module_id' => $moduleConfig->id], + ['configkey' => '_CACHE_ENABLED_', 'configvalue' => false, 'name' => json_encode(['en' => 'Enable Cache', 'es' => 'Habilitar Caché']), 'description' => json_encode(['en' => 'Enable queries caching to increase performance', 'es' => 'Habilitar el caché de consultas para mejorar el rendimiento']), 'module_id' => $moduleConfig->id], ], ['configkey'], ['configvalue', 'name', 'description', 'module_id']); } } diff --git a/src/Facades/PbDebugbarFacade.php b/src/Facades/PbDebugbarFacade.php new file mode 100644 index 0000000..137dbde --- /dev/null +++ b/src/Facades/PbDebugbarFacade.php @@ -0,0 +1,13 @@ + "string", 'country' => "string"])] function getDefaultCountry(string $code): array +#[ArrayShape(['code' => "string", 'country' => "string"])] +function getDefaultCountry(string $code): array { if (file_exists(base_path('/node_modules/flag-icons/country.json'))) { $data = json_decode(file_get_contents(base_path('/node_modules/flag-icons/country.json')), true); @@ -89,7 +91,7 @@ function getRoutesNames(): array } } } - $default = ($countries['gb'] ?? ($countries['es'] ) ?? ['1x1' => "" , '4x3' => ""]); + $default = ($countries['gb'] ?? $countries['es'] ?? ['1x1' => "" , '4x3' => ""]); return [ 'code' => $code, 'country' => match ($code) { @@ -109,7 +111,8 @@ function getRoutesNames(): array * * @return array */ -#[ArrayShape(['code' => "string", 'country' => "string"])] function getDefaultCountryFromCurrentLocale(): array +#[ArrayShape(['code' => "string", 'country' => "string"])] +function getDefaultCountryFromCurrentLocale(): array { return getDefaultCountry(app()->getLocale()); } @@ -120,7 +123,8 @@ function getRoutesNames(): array * @param $value * @return array */ -#[ArrayShape(['web' => "string[]", 'api' => "string[]", 'auth' => "string[]", 'debug' => "string[]"])] function getDefaultGroupsMiddlewares($value): array +#[ArrayShape(['web' => "string[]", 'api' => "string[]", 'auth' => "string[]", 'debug' => "string[]"])] +function getDefaultGroupsMiddlewares($value): array { return match($value) { 'web' => ['web', 'set_config_data', 'check_https', 'single_session', 'set_locale'], @@ -148,7 +152,8 @@ function isApi(Request $request): bool * @param $var * @return string */ -#[NoReturn] function prettyDie($var): string +#[NoReturn] +function prettyDie($var): string { header('Content-Type: application/json; charset=utf-8'); die(json_encode( @@ -207,7 +212,8 @@ function getMigrationFileName($filename, $offset): string 'create' => "\string[][]", 'update' => "\string[][]", 'delete' => "\string[][]" -])] function methodsByPermission(): array +])] +function methodsByPermission(): array { return [ 'read' => [], @@ -286,3 +292,28 @@ function getConfigValue($key): mixed return PbConfig::findByKey($key)->configvalue; } + +/** + * Scope a query to only include popular users. + * + * @param $class + * @return string + * @throws ReflectionException + */ +function getClassName($class): string +{ + return (new ReflectionClass($class))->getShortName(); +} + +/** + * Scope a query to only include popular users. + * + * @param $class + * @param $function + * @return string + * @throws ReflectionException + */ +function getFunctionName($class, $function): string +{ + return (new ReflectionClass($class))->getMethod($function)->getName(); +} diff --git a/src/Interfaces/PbModelCrudInterface.php b/src/Interfaces/PbModelCrudInterface.php index b55f99a..10ff423 100644 --- a/src/Interfaces/PbModelCrudInterface.php +++ b/src/Interfaces/PbModelCrudInterface.php @@ -14,4 +14,11 @@ interface PbModelCrudInterface * @return bool|PbUser|PbCurrentUser */ public function getAuthorizedUser($id): bool|PbUser|PbCurrentUser; + + /** + * Scope a query to only include popular users. + * + * @return array + */ + public static function getCrudConfig(): array; } diff --git a/src/Middleware/PbIsDebugModeEnabledMiddleware.php b/src/Middleware/PbIsDebugModeEnabledMiddleware.php index 6d8b4f2..c6f40bf 100644 --- a/src/Middleware/PbIsDebugModeEnabledMiddleware.php +++ b/src/Middleware/PbIsDebugModeEnabledMiddleware.php @@ -2,7 +2,7 @@ namespace Anibalealvarezs\Projectbuilder\Middleware; -use Anibalealvarezs\Projectbuilder\Overrides\Classes\PbDebugbar; +use Anibalealvarezs\Projectbuilder\Facades\PbDebugbarFacade as Debug; use Closure; use Illuminate\Http\Request; @@ -17,7 +17,7 @@ class PbIsDebugModeEnabledMiddleware */ public function handle($request, Closure $next) { - PbDebugbar::toggleStatus(); + Debug::toggleStatus(); return $next($request); } } diff --git a/src/Models/PbBuilder.php b/src/Models/PbBuilder.php index 2aab9eb..76a1e9a 100644 --- a/src/Models/PbBuilder.php +++ b/src/Models/PbBuilder.php @@ -31,7 +31,8 @@ class PbBuilder extends Model 'pagination' => "array", 'heading' => "array", 'orderable' => "bool", - ])] public static function getCrudConfig(bool $includeForm = false): array + ])] + public static function getCrudConfig(bool $includeForm = false): array { return [ 'default' => [], diff --git a/src/Overrides/Classes/PbDebugbar.php b/src/Overrides/Classes/PbDebugbar.php index 03a651a..f03da34 100644 --- a/src/Overrides/Classes/PbDebugbar.php +++ b/src/Overrides/Classes/PbDebugbar.php @@ -4,9 +4,10 @@ use Anibalealvarezs\Projectbuilder\Models\PbCurrentUser; use Barryvdh\Debugbar\Facades\Debugbar; +use Closure; use Illuminate\Support\Facades\Auth; -class PbDebugbar extends Debugbar +class PbDebugbar { /** * Transform the resource into an array. @@ -16,21 +17,7 @@ class PbDebugbar extends Debugbar public static function toggleStatus(): void { if(!self::isDebugEnabled()) { - self::disable(); - } - } - - /** - * Transform the resource into an array. - * - * @param mixed $message - * @param string $label - * @return void - */ - public static function addMessage(mixed $message, string $label = 'info'): void - { - if (self::isDebugEnabled()) { - Debugbar::addMessage($message, $label); + Debugbar::disable(); } } @@ -61,4 +48,57 @@ public static function getDebugStatus(): bool { return (bool) getConfigValue('_DEBUG_MODE_'); } + + /** + * Transform the resource into an array. + * + * @param mixed $message + * @param string $label + * @param bool|null $cacheOnly + * @return void + */ + public static function add(mixed $message, string $label = 'info', bool $cacheOnly = null): void + { + if (self::isDebugEnabled() && (!$cacheOnly || getConfigValue('_CACHE_ENABLED_'))) { + Debugbar::addMessage($message, $label); + } + } + + /** + * Starts a measure + * + * @param string $name Internal name, used to stop the measure + * @param string|null $label Public name + */ + public function start(string $name, string $label = null) + { + if (self::isDebugEnabled()) { + Debugbar::startMeasure($name, $label); + } + } + + /** + * Stops a measure + * + * @param string $name + */ + public function stop(string $name) + { + if (self::isDebugEnabled()) { + Debugbar::stopMeasure($name); + } + } + + /** + * Stops a measure + * + * @param $label + * @param Closure $closure + */ + public function measure($label, Closure $closure) + { + if (self::isDebugEnabled()) { + Debugbar::measure($label, $closure); + } + } } diff --git a/src/Providers/PbAppServiceProvider.php b/src/Providers/PbAppServiceProvider.php index e59a183..6d676af 100644 --- a/src/Providers/PbAppServiceProvider.php +++ b/src/Providers/PbAppServiceProvider.php @@ -3,6 +3,7 @@ namespace Anibalealvarezs\Projectbuilder\Providers; use Anibalealvarezs\Projectbuilder\Facades\PbUtilitiesFacade; +use Anibalealvarezs\Projectbuilder\Facades\PbDebugbarFacade as Debug; use Anibalealvarezs\Projectbuilder\Overrides\Classes\PbDebugbar; use Anibalealvarezs\Projectbuilder\Utilities\PbUtilities; use Anibalealvarezs\Projectbuilder\Models\PbConfig; @@ -51,7 +52,7 @@ public function boot() } } $locale = getDefaultCountryFromCurrentLocale(); - PbDebugbar::addMessage($locale, 'locale'); + Debug::add($locale, 'locale'); return $locale; }, 'teams' => function () { @@ -71,6 +72,9 @@ public function register() $this->app->bind('PbUtilities', function () { return new PbUtilities(); }); + $this->app->bind('PbDebugbar', function () { + return new PbDebugbar(); + }); $this->app->singleton(PbUtilities::class, static fn() => new PbUtilities()); $this->app->singleton(PbUtilitiesFacade::class, static fn() => new PbUtilitiesFacade()); } diff --git a/src/Traits/PbControllerListingTrait.php b/src/Traits/PbControllerListingTrait.php index f9af963..9c32939 100644 --- a/src/Traits/PbControllerListingTrait.php +++ b/src/Traits/PbControllerListingTrait.php @@ -171,12 +171,12 @@ protected static function buildListingField(array $options = []): array return [ "key" => ($options['key'] ?? ""), - "name" => ($options['name'] ?? ($options['key'] ? ucwords($options['key']) : "")), + "name" => (isset($options['name']) && $options['name'] ? $options['name'] : ($options['key'] ? ucwords($options['key']) : "")), "arrval" => ($options['arrval'] ?? []), "style" => ($options['style'] ?? []), "buttons" => ($options['buttons'] ?? []), "href" => ($options['href'] ?? []), - "size" => ($options['size'] ?? 'single'), + "size" => (isset($options['size']) && $options['size'] ? $options['size'] : 'single'), "status" => ($options['status'] ?? false), "orderable" => ($options['orderable'] ?? false), ]; diff --git a/src/Traits/PbControllerTrait.php b/src/Traits/PbControllerTrait.php index 1c33001..189cb43 100644 --- a/src/Traits/PbControllerTrait.php +++ b/src/Traits/PbControllerTrait.php @@ -267,9 +267,17 @@ public function handleJSONError($error, array $errorMsg = [], int $code = 404): * @param array $defaultOrder * @return LengthAwarePaginator */ - public function paginateAndOrder(Builder $query, int $page = 1, int $perpage = 0, string $orderby = null, string $field = 'id', string $order = 'asc', array $defaultOrder = []): LengthAwarePaginator + public function paginateAndOrder( + Builder $query, + int $page = 1, + int $perpage = 0, + string $orderby = null, + string $field = 'id', + string $order = 'asc', + array $defaultOrder = [] + ): LengthAwarePaginator { - $config = ($this->vars->config ?? $this->vars->level->modelPath::getCrudConfig(true)); + $config = (isset($this->vars->config) && $this->vars->config) ? $this->vars->config : $this->vars->level->modelPath::getCrudConfig(true); if (!$perpage && isset($config['pagination']['per_page']) && $config['pagination']['per_page']) { $perpage = $config['pagination']['per_page']; } @@ -295,10 +303,26 @@ public function paginateAndOrder(Builder $query, int $page = 1, int $perpage = 0 * @param array $defaultOrder * @return LengthAwarePaginator|Collection */ - public function buildPaginatedAndOrderedModel(Builder $query = null, int $page = 1, int $perpage = 0, string $orderby = null, string $field = 'id', string $order = 'asc', array $defaultOrder = []): LengthAwarePaginator|Collection + public function buildPaginatedAndOrderedModel( + Builder $query = null, + int $page = 1, + int $perpage = 0, + string $orderby = null, + string $field = 'id', + string $order = 'asc', + array $defaultOrder = [] + ): LengthAwarePaginator|Collection { if (!isset($this->vars->level->modelPath::$sortable) || !$this->vars->level->modelPath::$sortable) { - return $this->paginateAndOrder($query ?: $this->vars->level->modelPath::withPublicRelations(), $page, $perpage, $orderby, $field, $order, $defaultOrder); + return $this->paginateAndOrder( + query: $query ?: $this->vars->level->modelPath::withPublicRelations(), + page: $page, + perpage: $perpage, + orderby: $orderby, + field: $field, + order: $order, + defaultOrder: $defaultOrder + ); } else { return $query->get(); } diff --git a/src/Utilities/PbCache.php b/src/Utilities/PbCache.php new file mode 100644 index 0000000..131e4fd --- /dev/null +++ b/src/Utilities/PbCache.php @@ -0,0 +1,146 @@ +roles->pluck('name')->implode('_'); + } elseif ($byUser) { + $index .= ':user:' . Auth::id(); + } + return $index; + } + + /** + * Scope a query to only include popular users. + * + * @param Closure $closure + * @param string $package + * @param string $type + * @param string|null $class + * @param string $function + * @param string|null $model + * @param string|null $modelFunction + * @param int $modelId + * @param array $pagination + * @param bool $byRoles + * @param bool $byUser + * @param bool $toArray + * @return array + * @throws InvalidArgumentException + * @throws ReflectionException + */ + #[ArrayShape(['data' => "mixed", 'index' => "null|string"])] + public static function run( + Closure $closure, + string $package, + string $type = 'controller', + string $class = null, + string $function = 'index', + string $model = null, + string $modelFunction = null, + int $modelId = 0, + array $pagination = [], + bool $byRoles = false, + bool $byUser = false, + bool $toArray = null + ): array + { + $index = self::buildCacheIndex( + package: $package, + type: $type, + class: Str::lower(getClassName($class)), + function: Str::lower(getFunctionName($class, $function)), + model: $model, + modelFunction: $modelFunction, + modelId: $modelId, + pagination: $pagination, + byRoles: $byRoles, + byUser: $byUser + ); + $indexedCache = false; + + // if (false) { + if (getConfigValue('_CACHE_ENABLED_') && Cache::store('redis')->has($index)) { + $indexedCache = true; + $data = unserialize(Cache::store('redis')->get($index)); + } + + $result = $data ?? self::processClosure($closure, $index); + + return ['data' => $result && $toArray ? $result->toArray() : $result, 'index' => ($indexedCache ? $index : null)]; + } + + /** + * Scope a query to only include popular users. + * + * @param Closure $closure + * @param string $index + * @return array|object|null + * @throws InvalidArgumentException + */ + public static function processClosure(Closure $closure, string $index): array|object|null + { + $data = $closure(); + if (getConfigValue('_CACHE_ENABLED_')) { + Cache::store('redis')->set($index, serialize($data)); + } + return $data; + } +} diff --git a/src/Utilities/PbShares.php b/src/Utilities/PbShares.php index 1cfb716..5482219 100644 --- a/src/Utilities/PbShares.php +++ b/src/Utilities/PbShares.php @@ -9,7 +9,7 @@ use Anibalealvarezs\Projectbuilder\Models\PbNavigation; use Anibalealvarezs\Projectbuilder\Models\PbPermission; use Anibalealvarezs\Projectbuilder\Models\PbRole; -use Anibalealvarezs\Projectbuilder\Overrides\Classes\PbDebugbar; +use Anibalealvarezs\Projectbuilder\Facades\PbDebugbarFacade as Debug; use JetBrains\PhpStorm\ArrayShape; class PbShares @@ -35,7 +35,7 @@ public static function list($elements): array "countries" => [...$list, ...self::getCountries()], "me" => [...$list, ...self::getMyData()], "api_data" => [...$list, ...self::apiData()], - "debug_status" => [...$list, ...['debug_enabled' => PbDebugbar::isDebugEnabled()]], + "debug_status" => [...$list, ...['debug_enabled' => Debug::isDebugEnabled()]], "modules" => [...$list, ...self::getModules()], "modules_replace" => [...$list, ...self::getModulesReplacingIds()], };