diff --git a/.prettierrc.yml b/.prettierrc.yml index 12bd80ffe..f9fd4f5c0 100644 --- a/.prettierrc.yml +++ b/.prettierrc.yml @@ -1,9 +1,13 @@ +### General trailingComma: all printWidth: 80 tabWidth: 4 useTabs: false singleQuote: true -trailingCommaPHP: php5 -braceStyle: psr-2 requirePragma: false insertPragma: false +semi: false + +### PHP +trailingCommaPHP: true +braceStyle: psr-2 diff --git a/config/capsules.php b/config/capsules.php new file mode 100644 index 000000000..ac2d2902f --- /dev/null +++ b/config/capsules.php @@ -0,0 +1,39 @@ + app_path('Twill/Capsules'), + + 'list' => [ + // ['name' => 'Posts', 'enabled' => true], + ], +]; + +/// To fully override this config: +/// +//[ +// 'modules' => [ +// 'path' => app_path('Twill/Modules'), +// +// 'loaded' = true, +// +// 'list' => [ +// [ +// "name" => "Post", +// "enabled" => true, +// "psr4_path" => "/app-path/app/Twill/Modules/Post/app", +// "namespace" => "App\Twill\Modules\Post", +// "root_path" => "/app-path/app/Twill/Modules/Post", +// "migrations_dir" => "/app-path/app/Twill/Modules/Post/database/migrations", +// "views_dir" => "/app-path/app/Twill/Modules/Post/resources/views", +// "view_prefix" => "Post.resources.views.admin", +// "routes_file" => "/app-path/app/Twill/Modules/Post/routes/admin.php", +// "model" => "App\Twill\Modules\Post\Data\Models\Post", +// "translation" => "App\Twill\Modules\Post\Data\Models\PostTranslation", +// "slug" => "App\Twill\Modules\Post\Data\Models\PostSlug", +// "revision" => "App\Twill\Modules\Post\Data\Models\PostRevision", +// "repository" => "App\Twill\Modules\Post\Data\Repositories\PostRepository", +// "controller" => "App\Twill\Modules\Post\Http\Controllers\PostController", +// "formRequest" => "App\Twill\Modules\Post\Http\Requests\PostRequest", +// ], +// ], +// ]; diff --git a/src/CapsulesServiceProvider.php b/src/CapsulesServiceProvider.php new file mode 100644 index 000000000..97e0513c7 --- /dev/null +++ b/src/CapsulesServiceProvider.php @@ -0,0 +1,89 @@ +mergeConfigFrom( + __DIR__ . '/../config/capsules.php', + 'twill.capsules' + ); + + $this->app + ->make('config') + ->set('twill.capsules.list', $this->getCapsuleList()); + + $this->app->make('config')->set('twill.capsules.loaded', true); + } + + public function register() + { + $this->registerConfig(); + } + + protected function registerConfig() + { + $this->mergeTwillConfig(); + + $this->registerCapsules(); + + $this->registerViewPaths(); + + $this->registerManager(); + } + + public function registerCapsules() + { + $this->getCapsuleList()->map(function ($capsule) { + $this->registerCapsule($capsule); + }); + } + + protected function registerCapsule($capsule) + { + $this->loadMigrationsFrom($capsule['migrations_dir']); + } + + public function map(Router $router) + { + $this->getCapsuleList()->each(function ($capsule) use ($router) { + $this->registerCapsuleRoutes($router, $capsule); + }); + } + + public function registerCapsuleRoutes($router, $capsule) + { + $this->registerRoutes( + $router, + $this->getRouteGroupOptions(), + $this->getRouteMiddleware(), + $this->supportSubdomainRouting(), + "{$capsule['namespace']}\Http\Controllers", + $capsule['routes_file'] + ); + } + + public function registerViewPaths() + { + $this->callAfterResolving('view', function ($view) { + $view->addLocation(config('twill.capsules.path')); + }); + } + + public function registerManager() + { + $this->app->instance('twill.capsules.manager', new Manager()); + } +} diff --git a/src/Helpers/frontend_helpers.php b/src/Helpers/frontend_helpers.php index be723f8b1..fb84fc91b 100644 --- a/src/Helpers/frontend_helpers.php +++ b/src/Helpers/frontend_helpers.php @@ -57,3 +57,18 @@ function icon($name, $opts = []) return ""; } } + +if (!function_exists('twillViewName')) { + function twillViewName($module, $suffix) + { + $view = "'admin.'.$module.'.{$suffix}'"; + + if (view()->exists($view)) { + return $view; + } + + $prefix = app('twill.capsules.manager')->getCapsuleViewPrefix($module); + + return "{$prefix}.{$suffix}"; + } +} diff --git a/src/Http/Controllers/Admin/ModuleController.php b/src/Http/Controllers/Admin/ModuleController.php index 0c1fb7fc5..b69cc1b21 100644 --- a/src/Http/Controllers/Admin/ModuleController.php +++ b/src/Http/Controllers/Admin/ModuleController.php @@ -3,6 +3,7 @@ namespace A17\Twill\Http\Controllers\Admin; use A17\Twill\Helpers\FlashLevel; +use A17\Twill\Services\Capsules\HasCapsules; use A17\Twill\Services\Blocks\BlockCollection; use A17\Twill\Models\Behaviors\HasSlug; use Illuminate\Contracts\Foundation\Application; @@ -23,6 +24,8 @@ abstract class ModuleController extends Controller { + use HasCapsules; + /** * @var Application */ @@ -1556,7 +1559,18 @@ protected function validateFormRequest() $this->request->offsetUnset($field); }); - return App::make("$this->namespace\Http\Requests\Admin\\" . $this->modelName . "Request"); + return App::make($this->getFormRequestClass()); + } + + public function getFormRequestClass() + { + $request = "$this->namespace\Http\Requests\Admin\\" . $this->modelName . "Request"; + + if (@class_exists($request)) { + return $request; + } + + return $this->getCapsuleFormRequestClass($this->modelName); } /** @@ -1617,7 +1631,17 @@ protected function getModelName() */ protected function getRepository() { - return App::make("$this->namespace\Repositories\\" . $this->modelName . "Repository"); + return App::make($this->getRepositoryClass($this->modelName)); + } + + public function getRepositoryClass($model) + { + if (@class_exists($class = "$this->namespace\Repositories\\" . $model . "Repository")) + { + return $class; + } + + return $this->getCapsuleRepositoryClass($model); } /** @@ -1625,7 +1649,13 @@ protected function getRepository() */ protected function getViewPrefix() { - return "admin.$this->moduleName"; + $prefix = "admin.$this->moduleName"; + + if (view()->exists("$prefix.form")) { + return $prefix; + } + + return $this->getCapsuleViewPrefix($this->moduleName); } /** diff --git a/src/Models/Behaviors/HasRevisions.php b/src/Models/Behaviors/HasRevisions.php index 7082a1062..ba62730ac 100644 --- a/src/Models/Behaviors/HasRevisions.php +++ b/src/Models/Behaviors/HasRevisions.php @@ -6,7 +6,7 @@ trait HasRevisions { public function revisions() { - return $this->hasMany(config('twill.namespace') . "\Models\Revisions\\" . class_basename($this) . "Revision")->orderBy('created_at', 'desc'); + return $this->hasMany($this->getRevisionModel())->orderBy('created_at', 'desc'); } public function scopeMine($query) @@ -26,4 +26,16 @@ public function revisionsArray() ]; })->toArray(); } + + protected function getRevisionModel() + { + $revision = config('twill.namespace') . "\Models\Revisions\\" . class_basename($this) . "Revision"; + + if (@class_exists($revision)) + { + return $revision; + } + + return $this->getCapsuleRevisionClass(class_basename($this)); + } } diff --git a/src/Models/Behaviors/HasSlug.php b/src/Models/Behaviors/HasSlug.php index 4ff9884ea..064d69029 100644 --- a/src/Models/Behaviors/HasSlug.php +++ b/src/Models/Behaviors/HasSlug.php @@ -2,9 +2,8 @@ namespace A17\Twill\Models\Behaviors; -use Illuminate\Support\Str; use Illuminate\Support\Facades\DB; - +use Illuminate\Support\Str; trait HasSlug { @@ -27,15 +26,23 @@ protected static function bootHasSlug() public function slugs() { - return $this->hasMany( - $this->getNamespace() . "\Slugs\\" . $this->getSlugClassName() - ); + return $this->hasMany($this->getSlugModelClass()); } public function getSlugClass() { - $slugClassName = $this->getNamespace() . "\Slugs\\" . $this->getSlugClassName(); - return new $slugClassName; + return new $this->getSlugModelClass(); + } + + public function getSlugModelClass() + { + $slug = $this->getNamespace() . "\Slugs\\" . $this->getSlugClassName(); + + if (@class_exists()) { + return $slug; + } + + return $this->getCapsuleSlugClass(class_basename($this)); } protected function getSlugClassName() diff --git a/src/Models/Behaviors/HasTranslation.php b/src/Models/Behaviors/HasTranslation.php index 66d471133..65400f322 100644 --- a/src/Models/Behaviors/HasTranslation.php +++ b/src/Models/Behaviors/HasTranslation.php @@ -4,14 +4,21 @@ use Astrotomic\Translatable\Translatable; use Illuminate\Database\Query\JoinClause; +use A17\Twill\Services\Capsules\HasCapsules; trait HasTranslation { - use Translatable; + use Translatable, HasCapsules; public function getTranslationModelNameDefault() { - return config('twill.namespace') . "\Models\Translations\\" . class_basename($this) . 'Translation'; + $repository = config('twill.namespace') . "\Models\Translations\\" . class_basename($this) . 'Translation'; + + if (@class_exists($repository)) { + return $repository; + } + + return $this->getCapsuleTranslationClass(class_basename($this)); } public function scopeWithActiveTranslations($query, $locale = null) diff --git a/src/Models/Model.php b/src/Models/Model.php index 914267813..df9d32146 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -18,6 +18,11 @@ abstract class Model extends BaseModel implements TaggableInterface public $timestamps = true; + protected function isTranslationModel() + { + return Str::endsWith(get_class($this), 'Translation'); + } + public function scopePublished($query) { return $query->wherePublished(true); @@ -75,8 +80,8 @@ public function getFillable() // Use the list of translatable attributes on our base model if ( blank($fillable) && - Str::contains($class = get_class($this), 'Models\Translations') && - property_exists($class, 'baseModuleModel') + $this->isTranslationModel() && + property_exists($this, 'baseModuleModel') ) { $fillable = (new $this->baseModuleModel)->getTranslatedAttributes(); diff --git a/src/RouteServiceProvider.php b/src/RouteServiceProvider.php index fe5ec19ac..24b52a590 100644 --- a/src/RouteServiceProvider.php +++ b/src/RouteServiceProvider.php @@ -8,6 +8,7 @@ use A17\Twill\Http\Middleware\RedirectIfAuthenticated; use A17\Twill\Http\Middleware\SupportSubdomainRouting; use A17\Twill\Http\Middleware\ValidateBackHistory; +use A17\Twill\Services\Routing\HasRoutes; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Routing\Router; use Illuminate\Support\Arr; @@ -16,6 +17,8 @@ class RouteServiceProvider extends ServiceProvider { + use HasRoutes; + protected $namespace = 'A17\Twill\Http\Controllers'; /** @@ -36,109 +39,139 @@ public function boot() */ public function map(Router $router) { - if (($patterns = config('twill.admin_route_patterns')) != null) { - if (is_array($patterns)) { - foreach ($patterns as $label => $pattern) { - Route::pattern($label, $pattern); - } - } - } - - $groupOptions = [ - 'as' => 'admin.', - 'middleware' => [config('twill.admin_middleware_group', 'web')], - 'prefix' => rtrim(ltrim(config('twill.admin_app_path'), '/'), '/'), - ]; - - $middlewares = [ - 'twill_auth:twill_users', - 'impersonate', - 'validateBackHistory', - 'localization', - ]; - - $supportSubdomainRouting = config('twill.support_subdomain_admin_routing', false); - - if ($supportSubdomainRouting) { - array_unshift($middlewares, 'supportSubdomainRouting'); - } - - $this->mapHostRoutes($router, $groupOptions, $middlewares, $supportSubdomainRouting); - $this->mapInternalRoutes($router, $groupOptions, $middlewares, $supportSubdomainRouting); + $this->registerRoutePatterns(); + + $this->mapHostRoutes( + $router, + $this->getRouteGroupOptions(), + $this->getRouteMiddleware(), + $this->supportSubdomainRouting() + ); + + $this->mapInternalRoutes( + $router, + $this->getRouteGroupOptions(), + $this->getRouteMiddleware(), + $this->supportSubdomainRouting() + ); } - private function mapHostRoutes($router, $groupOptions, $middlewares, $supportSubdomainRouting) - { - if (file_exists(base_path('routes/admin.php'))) { - $hostRoutes = function ($router) use ($middlewares) { - $router->group([ - 'namespace' => config('twill.namespace', 'App') . '\Http\Controllers\Admin', - 'middleware' => $middlewares, - ], function ($router) { - require base_path('routes/admin.php'); - }); - }; - - $router->group($groupOptions + [ - 'domain' => config('twill.admin_app_url'), - ], $hostRoutes); - - if ($supportSubdomainRouting) { - $router->group($groupOptions + [ - 'domain' => config('twill.admin_app_subdomain', 'admin') . '.{subdomain}.' . config('app.url'), - ], $hostRoutes); - } - } + private function mapHostRoutes( + $router, + $groupOptions, + $middlewares, + $supportSubdomainRouting, + $namespace = null + ) { + $this->registerRoutes( + $router, + $groupOptions, + $middlewares, + $supportSubdomainRouting, + config('twill.namespace', 'App') . '\Http\Controllers\Admin', + base_path('routes/admin.php') + ); } - private function mapInternalRoutes($router, $groupOptions, $middlewares, $supportSubdomainRouting) - { - $internalRoutes = function ($router) use ($middlewares, $supportSubdomainRouting) { + private function mapInternalRoutes( + $router, + $groupOptions, + $middlewares, + $supportSubdomainRouting + ) { + $internalRoutes = function ($router) use ( + $middlewares, + $supportSubdomainRouting + ) { $router->group(['middleware' => $middlewares], function ($router) { require __DIR__ . '/../routes/admin.php'; }); - $router->group([ - 'middleware' => $supportSubdomainRouting ? ['supportSubdomainRouting'] : [], - ], function ($router) { - require __DIR__ . '/../routes/auth.php'; - }); + $router->group( + [ + 'middleware' => $supportSubdomainRouting + ? ['supportSubdomainRouting'] + : [], + ], + function ($router) { + require __DIR__ . '/../routes/auth.php'; + } + ); - $router->group(['middleware' => $this->app->environment('production') ? ['twill_auth:twill_users'] : []], function ($router) { - require __DIR__ . '/../routes/templates.php'; - }); + $router->group( + [ + 'middleware' => $this->app->environment('production') + ? ['twill_auth:twill_users'] + : [], + ], + function ($router) { + require __DIR__ . '/../routes/templates.php'; + } + ); }; - $router->group($groupOptions + [ - 'namespace' => $this->namespace . '\Admin', - ], function ($router) use ($internalRoutes, $supportSubdomainRouting) { - $router->group([ - 'domain' => config('twill.admin_app_url'), - ], $internalRoutes); - - if ($supportSubdomainRouting) { - $router->group([ - 'domain' => config('twill.admin_app_subdomain', 'admin') . '.{subdomain}.' . config('app.url'), - ], $internalRoutes); + $router->group( + $groupOptions + [ + 'namespace' => $this->namespace . '\Admin', + ], + function ($router) use ($internalRoutes, $supportSubdomainRouting) { + $router->group( + [ + 'domain' => config('twill.admin_app_url'), + ], + $internalRoutes + ); + + if ($supportSubdomainRouting) { + $router->group( + [ + 'domain' => + config('twill.admin_app_subdomain', 'admin') . + '.{subdomain}.' . + config('app.url'), + ], + $internalRoutes + ); + } } - }); + ); if (config('twill.templates_on_frontend_domain')) { - $router->group([ - 'namespace' => $this->namespace . '\Admin', - 'domain' => config('app.url'), - 'middleware' => [config('twill.admin_middleware_group', 'web')], - ], + $router->group( + [ + 'namespace' => $this->namespace . '\Admin', + 'domain' => config('app.url'), + 'middleware' => [ + config('twill.admin_middleware_group', 'web'), + ], + ], function ($router) { - $router->group(['middleware' => $this->app->environment('production') ? ['twill_auth:twill_users'] : []], function ($router) { - require __DIR__ . '/../routes/templates.php'; - }); + $router->group( + [ + 'middleware' => $this->app->environment( + 'production' + ) + ? ['twill_auth:twill_users'] + : [], + ], + function ($router) { + require __DIR__ . '/../routes/templates.php'; + } + ); } ); } - if (config('twill.media_library.image_service') === 'A17\Twill\Services\MediaLibrary\Glide') { - $router->get('/' . config('twill.glide.base_path') . '/{path}', GlideController::class)->where('path', '.*'); + if ( + config('twill.media_library.image_service') === + 'A17\Twill\Services\MediaLibrary\Glide' + ) { + $router + ->get( + '/' . config('twill.glide.base_path') . '/{path}', + GlideController::class + ) + ->where('path', '.*'); } } @@ -150,11 +183,20 @@ function ($router) { */ private function registerRouteMiddlewares(Router $router) { - Route::aliasMiddleware('supportSubdomainRouting', SupportSubdomainRouting::class); + Route::aliasMiddleware( + 'supportSubdomainRouting', + SupportSubdomainRouting::class + ); Route::aliasMiddleware('impersonate', Impersonate::class); - Route::aliasMiddleware('twill_auth', \Illuminate\Auth\Middleware\Authenticate::class); + Route::aliasMiddleware( + 'twill_auth', + \Illuminate\Auth\Middleware\Authenticate::class + ); Route::aliasMiddleware('twill_guest', RedirectIfAuthenticated::class); - Route::aliasMiddleware('validateBackHistory', ValidateBackHistory::class); + Route::aliasMiddleware( + 'validateBackHistory', + ValidateBackHistory::class + ); Route::aliasMiddleware('localization', Localization::class); } @@ -165,7 +207,11 @@ private function registerRouteMiddlewares(Router $router) */ protected function registerMacros() { - Route::macro('moduleShowWithPreview', function ($moduleName, $routePrefix = null, $controllerName = null) { + Route::macro('moduleShowWithPreview', function ( + $moduleName, + $routePrefix = null, + $controllerName = null + ) { if ($routePrefix === null) { $routePrefix = $moduleName; } @@ -174,28 +220,71 @@ protected function registerMacros() $controllerName = ucfirst(Str::plural($moduleName)); } - $routePrefix = empty($routePrefix) ? '/' : (Str::startsWith($routePrefix, '/') ? $routePrefix : '/' . $routePrefix); - $routePrefix = Str::endsWith($routePrefix, '/') ? $routePrefix : $routePrefix . '/'; - - Route::name($moduleName . '.show')->get($routePrefix . '{slug}', $controllerName . 'Controller@show'); - Route::name($moduleName . '.preview')->get('/admin-preview' . $routePrefix . '{slug}', $controllerName . 'Controller@show')->middleware(['web', 'twill_auth:twill_users', 'can:list']); + $routePrefix = empty($routePrefix) + ? '/' + : (Str::startsWith($routePrefix, '/') + ? $routePrefix + : '/' . $routePrefix); + $routePrefix = Str::endsWith($routePrefix, '/') + ? $routePrefix + : $routePrefix . '/'; + + Route::name($moduleName . '.show')->get( + $routePrefix . '{slug}', + $controllerName . 'Controller@show' + ); + Route::name($moduleName . '.preview') + ->get( + '/admin-preview' . $routePrefix . '{slug}', + $controllerName . 'Controller@show' + ) + ->middleware(['web', 'twill_auth:twill_users', 'can:list']); }); - Route::macro('module', function ($slug, $options = [], $resource_options = [], $resource = true) { - + Route::macro('module', function ( + $slug, + $options = [], + $resource_options = [], + $resource = true + ) { $slugs = explode('.', $slug); - $prefixSlug = str_replace('.', "/", $slug); + $prefixSlug = str_replace('.', '/', $slug); $_slug = Arr::last($slugs); - $className = implode("", array_map(function ($s) { - return ucfirst(Str::singular($s)); - }, $slugs)); + $className = implode( + '', + array_map(function ($s) { + return ucfirst(Str::singular($s)); + }, $slugs) + ); - $customRoutes = $defaults = ['reorder', 'publish', 'bulkPublish', 'browser', 'feature', 'bulkFeature', 'tags', 'preview', 'restore', 'bulkRestore', 'forceDelete', 'bulkForceDelete', 'bulkDelete', 'restoreRevision', 'duplicate']; + $customRoutes = $defaults = [ + 'reorder', + 'publish', + 'bulkPublish', + 'browser', + 'feature', + 'bulkFeature', + 'tags', + 'preview', + 'restore', + 'bulkRestore', + 'forceDelete', + 'bulkForceDelete', + 'bulkDelete', + 'restoreRevision', + 'duplicate', + ]; if (isset($options['only'])) { - $customRoutes = array_intersect($defaults, (array) $options['only']); + $customRoutes = array_intersect( + $defaults, + (array) $options['only'] + ); } elseif (isset($options['except'])) { - $customRoutes = array_diff($defaults, (array) $options['except']); + $customRoutes = array_diff( + $defaults, + (array) $options['except'] + ); } // Get the current route groups @@ -204,19 +293,28 @@ protected function registerMacros() // Get the name prefix of the last group $lastRouteGroupName = end($routeGroups)['as'] ?? ''; - $groupPrefix = trim(str_replace('/', '.', Route::getLastGroupPrefix()), '.'); + $groupPrefix = trim( + str_replace('/', '.', Route::getLastGroupPrefix()), + '.' + ); if (!empty(config('twill.admin_app_path'))) { - $groupPrefix = ltrim(str_replace(config('twill.admin_app_path'), '', $groupPrefix), '.'); + $groupPrefix = ltrim( + str_replace( + config('twill.admin_app_path'), + '', + $groupPrefix + ), + '.' + ); } // Check if name will be a duplicate, and prevent if needed/allowed - if (!empty($groupPrefix) && - ( - blank($lastRouteGroupName) || + if ( + !empty($groupPrefix) && + (blank($lastRouteGroupName) || config('twill.allow_duplicates_on_route_names', true) || - (!Str::endsWith($lastRouteGroupName, ".{$groupPrefix}.")) - ) + !Str::endsWith($lastRouteGroupName, ".{$groupPrefix}.")) ) { $customRoutePrefix = "{$groupPrefix}.{$slug}"; $resourceCustomGroupPrefix = "{$groupPrefix}."; @@ -229,37 +327,63 @@ protected function registerMacros() foreach ($customRoutes as $route) { $routeSlug = "{$prefixSlug}/{$route}"; - $mapping = ['as' => $customRoutePrefix . ".{$route}", 'uses' => "{$className}Controller@{$route}"]; + $mapping = [ + 'as' => $customRoutePrefix . ".{$route}", + 'uses' => "{$className}Controller@{$route}", + ]; if (in_array($route, ['browser', 'tags'])) { Route::get($routeSlug, $mapping); } if (in_array($route, ['restoreRevision'])) { - Route::get($routeSlug . "/{id}", $mapping); + Route::get($routeSlug . '/{id}', $mapping); } - if (in_array($route, ['publish', 'feature', 'restore', 'forceDelete'])) { + if ( + in_array($route, [ + 'publish', + 'feature', + 'restore', + 'forceDelete', + ]) + ) { Route::put($routeSlug, $mapping); } if (in_array($route, ['duplicate'])) { - Route::put($routeSlug . "/{id}", $mapping); + Route::put($routeSlug . '/{id}', $mapping); } if (in_array($route, ['preview'])) { - Route::put($routeSlug . "/{id}", $mapping); + Route::put($routeSlug . '/{id}', $mapping); } - if (in_array($route, ['reorder', 'bulkPublish', 'bulkFeature', 'bulkDelete', 'bulkRestore', 'bulkForceDelete'])) { + if ( + in_array($route, [ + 'reorder', + 'bulkPublish', + 'bulkFeature', + 'bulkDelete', + 'bulkRestore', + 'bulkForceDelete', + ]) + ) { Route::post($routeSlug, $mapping); } } if ($resource) { - Route::group(['as' => $resourceCustomGroupPrefix], function () use ($slug, $className, $resource_options) { - Route::resource($slug, "{$className}Controller", $resource_options); - }); + Route::group( + ['as' => $resourceCustomGroupPrefix], + function () use ($slug, $className, $resource_options) { + Route::resource( + $slug, + "{$className}Controller", + $resource_options + ); + } + ); } }); } diff --git a/src/Services/Capsules/HasCapsules.php b/src/Services/Capsules/HasCapsules.php new file mode 100644 index 000000000..8ebec56dc --- /dev/null +++ b/src/Services/Capsules/HasCapsules.php @@ -0,0 +1,155 @@ +bound('autoloader') + ? app('autoloader') + : require base_path('vendor/autoload.php'); + } + + public function getCapsuleList() + { + $path = config('twill.capsules.path'); + + $list = collect(config('twill.capsules.list')); + + if (config('twill.capsules.loaded')) { + return $list; + } + + return $list + ->where('enabled', true) + ->map(function ($capsule) use ($path) { + return $this->makeCapsule($capsule, $path); + }); + } + + public function getCapsuleByModel($model) + { + return $this->getCapsuleList() + ->where('singular', $model) + ->first(); + } + + public function getCapsuleByModule($module) + { + return $this->getCapsuleList() + ->filter(function ($capsule) use ($module) { + return Str::lower($capsule['plural']) == Str::lower($module); + }) + ->first(); + } + + public function makeCapsule($capsule, $basePath) + { + $capsule['plural'] = $name = $capsule['name']; + + $capsule['singular'] = $singular = + $capsule['singular'] ?? Str::singular($name); + + $twillNamespace = config('twill.namespace'); + + $capsuleNamespace = "{$twillNamespace}\Twill\Capsules\\{$name}"; + + $models = "{$capsuleNamespace}\Data\Models"; + + $capsule['psr4_path'] = "$basePath/{$name}/app"; + + $capsule['namespace'] = $capsuleNamespace; + + $capsule['root_path'] = $this->capsuleRootPath($capsule); + + $capsule[ + 'migrations_dir' + ] = "{$capsule['root_path']}/database/migrations"; + + $capsule['views_dir'] = "{$capsule['root_path']}/resources/views"; + + $capsule['view_prefix'] = "{$name}.resources.views.admin"; + + $capsule['routes_file'] = "{$capsule['root_path']}/routes/admin.php"; + + $capsule['model'] = "{$capsuleNamespace}\\Data\Models\\{$singular}"; + + $capsule['translation'] = "{$models}\\{$singular}Translation"; + + $capsule['slug'] = "{$models}\\{$singular}Slug"; + + $capsule['revision'] = "{$models}\\{$singular}Revision"; + + $capsule[ + 'repository' + ] = "{$capsule['namespace']}\\Data\Repositories\\{$singular}Repository"; + + $capsule[ + 'controller' + ] = "{$capsule['namespace']}\\Http\Controllers\\{$singular}Controller"; + + $capsule[ + 'formRequest' + ] = "{$capsule['namespace']}\\Http\Requests\\{$singular}Request"; + + $this->setPsr4Autoloader($capsule); + + return $capsule; + } + + public function registerPsr4Autoloader($capsule) + { + $this->getAutoloader()->setPsr4( + $capsule['namespace'] . '\\', + $capsule['psr4_path'] + ); + } + + public function unregisterPsr4Autoloaders() + { + $this->getAutoloader()->setPsr4( + $capsule['namespace'] . '\\', + $capsule['psr4_path'] + ); + } + + public function capsuleRootPath($capsule) + { + return config('twill.capsules.path') . '/' . $capsule['name'] ?? null; + } + + public function getCapsuleRepositoryClass($model) + { + return $this->getCapsuleByModel($model)['repository'] ?? null; + } + + public function getCapsuleTranslationClass($model) + { + return $this->getCapsuleByModel($model)['translation'] ?? null; + } + + public function getCapsuleSlugClass($model) + { + return $this->getCapsuleByModel($model)['slug'] ?? null; + } + + public function getCapsuleRevisionClass($model) + { + return $this->getCapsuleByModel($model)['revision'] ?? null; + } + + public function getCapsuleFormRequestClass($model) + { + return $this->getCapsuleByModel($model)['formRequest'] ?? null; + } + + public function getCapsuleViewPrefix($capsule) + { + return $this->getCapsuleByModule(Str::studly($capsule))[ + 'view_prefix' + ] ?? null; + } +} diff --git a/src/Services/Capsules/Manager.php b/src/Services/Capsules/Manager.php new file mode 100644 index 000000000..2b7d1fcfd --- /dev/null +++ b/src/Services/Capsules/Manager.php @@ -0,0 +1,8 @@ +group( + [ + 'namespace' => $namespace, + 'middleware' => $middlewares, + ], + function ($router) use ($routesFile) { + require $routesFile; + } + ); + }; + + $router->group( + $groupOptions + [ + 'domain' => config('twill.admin_app_url'), + ], + $hostRoutes + ); + + if ($supportSubdomainRouting) { + $router->group( + $groupOptions + [ + 'domain' => + config('twill.admin_app_subdomain', 'admin') . + '.{subdomain}.' . + config('app.url'), + ], + $hostRoutes + ); + } + } + } + + public function registerRoutePatterns() + { + if (($patterns = config('twill.admin_route_patterns')) != null) { + if (is_array($patterns)) { + foreach ($patterns as $label => $pattern) { + Route::pattern($label, $pattern); + } + } + } + } + + /** + * @return array + */ + protected function getRouteGroupOptions(): array + { + $groupOptions = [ + 'as' => 'admin.', + 'middleware' => [config('twill.admin_middleware_group', 'web')], + 'prefix' => rtrim(ltrim(config('twill.admin_app_path'), '/'), '/'), + ]; + + return $groupOptions; + } + + public function getRouteMiddleware($middleware = null) + { + if (is_array($middleware)) { + return $middleware; + } + + $middleware = [ + 'twill_auth:twill_users', + 'impersonate', + 'validateBackHistory', + 'localization', + ]; + + if ($this->supportSubdomainRouting()) { + array_unshift($middlewares, 'supportSubdomainRouting'); + } + + return $middleware; + } + + public function supportSubdomainRouting() + { + return config('twill.support_subdomain_admin_routing', false); + } +} diff --git a/src/TwillServiceProvider.php b/src/TwillServiceProvider.php index db11fa328..d365db42a 100644 --- a/src/TwillServiceProvider.php +++ b/src/TwillServiceProvider.php @@ -11,6 +11,7 @@ use A17\Twill\Commands\ListBlocks; use A17\Twill\Commands\ListIcons; use A17\Twill\Commands\ModuleMake; +use A17\Twill\Services\Capsules\HasCapsules; use A17\Twill\Commands\ModuleMakeDeprecated; use A17\Twill\Commands\RefreshLQIP; use A17\Twill\Commands\SyncLang; @@ -37,6 +38,7 @@ class TwillServiceProvider extends ServiceProvider { + use HasCapsules; /** * The Twill version. @@ -57,6 +59,7 @@ class TwillServiceProvider extends ServiceProvider TranslatableServiceProvider::class, TagsServiceProvider::class, ActivitylogServiceProvider::class, + CapsulesServiceProvider::class, ]; private $migrationsCounter = 0; @@ -316,7 +319,7 @@ private function registerCommands() */ private function includeView($view, $expression) { - list($name) = str_getcsv($expression, ',', '\''); + [$name] = str_getcsv($expression, ',', '\''); $partialNamespace = view()->exists('admin.' . $view . $name) ? 'admin.' : 'twill::'; @@ -358,10 +361,10 @@ private function extendBlade() $expressionAsArray = str_getcsv($expression, ',', '\''); - list($moduleName, $viewName) = $expressionAsArray; + [$moduleName, $viewName] = $expressionAsArray; $partialNamespace = 'twill::partials'; - $viewModule = "'admin.'.$moduleName.'.{$viewName}'"; + $viewModule = "twillViewName($moduleName, '{$viewName}')"; $viewApplication = "'admin.partials.{$viewName}'"; $viewModuleTwill = "'twill::'.$moduleName.'.{$viewName}'"; $view = $partialNamespace . "." . $viewName; @@ -391,7 +394,7 @@ private function extendBlade() }); $blade->directive('pushonce', function ($expression) { - list($pushName, $pushSub) = explode(':', trim(substr($expression, 1, -1))); + [$pushName, $pushSub] = explode(':', trim(substr($expression, 1, -1))); $key = '__pushonce_' . $pushName . '_' . str_replace('-', '_', $pushSub); return "{$key})): \$__env->{$key} = 1; \$__env->startPush('{$pushName}'); ?>"; });