diff --git a/composer.json b/composer.json index e6eeed6..763619f 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "php": "^8.2", "illuminate/support": "^10.0|^11.0", "laravel/nova": "^4.0", + "novius/laravel-linkable": "^1.0", "novius/laravel-meta": "^1.0", "novius/laravel-nova-field-preview": "^2.0", "novius/laravel-nova-publishable": "^3.0", @@ -67,10 +68,6 @@ { "type": "composer", "url": "https://nova.laravel.com" - }, - { - "type": "vcs", - "url": "git@github.com:novius/laravel-meta.git" } ], "config": { diff --git a/src/LaravelNovaNewsServiceProvider.php b/src/LaravelNovaNewsServiceProvider.php index e6a5007..3ed8d0f 100644 --- a/src/LaravelNovaNewsServiceProvider.php +++ b/src/LaravelNovaNewsServiceProvider.php @@ -5,6 +5,7 @@ use Illuminate\Support\Facades\Validator; use Illuminate\Support\ServiceProvider; use Laravel\Nova\Nova; +use Novius\LaravelLinkable\Facades\Linkable; use Novius\LaravelNovaNews\Console\FrontControllerCommand; use Novius\LaravelNovaNews\Models\NewsCategory; use Novius\LaravelNovaNews\Models\NewsPost; @@ -24,12 +25,26 @@ public function register(): void */ public function boot(): void { - // Load Nova resources - Nova::resources(array_filter([ - NovaNews::getPostResource(), - NovaNews::getCategoryResource(), - NovaNews::getTagResource(), - ])); + $this->app->booted(function () { + // Load Nova resources + Nova::resources(array_filter([ + NovaNews::getPostResource(), + NovaNews::getCategoryResource(), + NovaNews::getTagResource(), + ])); + }); + + $this->app->booted(function () { + Linkable::addModels(array_filter([ + NovaNews::getPostModel(), + NovaNews::getCategoryModel(), + NovaNews::getTagModel(), + ])); + Linkable::addRoutes(array_flip(array_filter([ + trans('laravel-nova-news::crud-post.resource_label') => config('laravel-nova-news.front_routes_name.posts'), + trans('laravel-nova-news::crud-category.resource_label') => config('laravel-nova-news.front_routes_name.categories'), + ]))); + }); $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); $this->loadTranslationsFrom(__DIR__.'/../lang', 'laravel-nova-news'); diff --git a/src/Models/ModelWithUrl.php b/src/Models/ModelWithUrl.php deleted file mode 100644 index 86e6690..0000000 --- a/src/Models/ModelWithUrl.php +++ /dev/null @@ -1,105 +0,0 @@ -getFrontRouteName(); - $parameter = $this->getUrlParameter(); - - if ($routeName === null || ! $this->exists || ! $parameter) { - return null; - } - - return route($routeName, [ - $parameter => $this->slug, - ]); - } - - public function previewUrl(): ?string - { - $routeName = $this->getFrontRouteName(); - $parameter = $this->getUrlParameter(); - - if (empty($routeName) || ! $parameter || ! $this->exists) { - return null; - } - - $params = [ - $parameter => $this->slug, - ]; - - $guard = config('laravel-nova-news.guard_preview'); - if (empty($guard) && in_array(Publishable::class, class_uses_recursive($this), true) && ! $this->isPublished()) { - $params['previewToken'] = $this->preview_token; - } - - return route($routeName, $params); - } - - protected function getUrlParameter(): ?string - { - $parameter = $this->getFrontRouteParameter(); - if (! empty($parameter)) { - return $parameter; - } - - $routeName = $this->getFrontRouteName(); - if (empty($routeName)) { - return null; - } - - $route = Route::getRoutes()->getByName($routeName); - if (! $route) { - return null; - } - - if (! preg_match('/({\w+})/', $route->uri(), $matches)) { - return null; - } - - return substr($matches[0], 1, -1); - } - - public function resolveRouteBinding($value, $field = null) - { - $query = static::where('locale', app()->currentLocale()); - - if (in_array(Publishable::class, class_uses_recursive($this), true)) { - $guard = config('laravel-nova-news.guard_preview'); - if (! empty($guard) && Auth::guard($guard)->check()) { - return $this->resolveRouteBindingQuery($query, $value, $field)->first(); - } - - if (request()->has('previewToken')) { - $query->where(function (Builder $query) { - $query->published() - ->orWhere('preview_token', request()->get('previewToken')); - }); - - return $this->resolveRouteBindingQuery($query, $value, $field)->first(); - } - - return $this->resolveRouteBindingQuery($query->published(), $value, $field)->first(); - } - - return $this->resolveRouteBindingQuery($query, $value, $field)->first(); - } -} diff --git a/src/Models/NewsCategory.php b/src/Models/NewsCategory.php index 0bcd135..cb6beae 100644 --- a/src/Models/NewsCategory.php +++ b/src/Models/NewsCategory.php @@ -6,8 +6,11 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; +use Novius\LaravelLinkable\Configs\LinkableConfig; +use Novius\LaravelLinkable\Traits\Linkable; use Novius\LaravelMeta\Enums\IndexFollow; use Novius\LaravelMeta\MetaModelConfig; use Novius\LaravelMeta\Traits\HasMeta; @@ -47,11 +50,12 @@ * * @mixin Eloquent */ -class NewsCategory extends ModelWithUrl +class NewsCategory extends Model { use HasFactory; use HasMeta; use HasSlug; + use Linkable; use SoftDeletes; use Translatable; @@ -77,6 +81,11 @@ protected static function booted(): void }); } + public function getRouteKeyName(): string + { + return 'slug'; + } + public function getSlugOptions(): SlugOptions { return SlugOptions::create() @@ -96,14 +105,29 @@ public function getMetaConfig(): MetaModelConfig return $this->metaConfig; } - public function getFrontRouteName(): ?string - { - return config('laravel-nova-news.front_routes_name.category'); - } + protected ?LinkableConfig $_linkableConfig; - public function getFrontRouteParameter(): ?string + public function linkableConfig(): ?LinkableConfig { - return config('laravel-nova-news.front_routes_parameters.category'); + $route = config('laravel-nova-news.front_routes_name.post'); + $routeParameterName = config('laravel-nova-news.front_routes_parameters.post'); + if (empty($routeParameterName) && empty($route)) { + return null; + } + + if (! isset($this->_linkableConfig)) { + $this->_linkableConfig = new LinkableConfig( + routeName: $route, + routeParameterName: $routeParameterName, + optionLabel: 'name', + optionGroup: trans('laravel-nova-news::crud-category.resource_label'), + resolveQuery: function (Builder|NewsCategory $query) { + $query->where('locale', app()->currentLocale()); + }, + ); + } + + return $this->_linkableConfig; } public function localParent() diff --git a/src/Models/NewsPost.php b/src/Models/NewsPost.php index 1554244..0138e62 100644 --- a/src/Models/NewsPost.php +++ b/src/Models/NewsPost.php @@ -6,11 +6,14 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; use Illuminate\Support\Str; +use Novius\LaravelLinkable\Configs\LinkableConfig; +use Novius\LaravelLinkable\Traits\Linkable; use Novius\LaravelMeta\Enums\IndexFollow; use Novius\LaravelMeta\MetaModelConfig; use Novius\LaravelMeta\Traits\HasMeta; @@ -71,11 +74,12 @@ * * @mixin Eloquent */ -class NewsPost extends ModelWithUrl +class NewsPost extends Model { use HasFactory; use HasMeta; use HasSlug; + use Linkable; use Publishable; use SoftDeletes; use Translatable; @@ -103,19 +107,14 @@ protected static function booted(): void }); } - public function isFeatured(): bool - { - return $this->featured; - } - - public function getFrontRouteName(): ?string + public function getRouteKeyName(): string { - return config('laravel-nova-news.front_routes_name.post'); + return 'slug'; } - public function getFrontRouteParameter(): ?string + public function isFeatured(): bool { - return config('laravel-nova-news.front_routes_parameters.post'); + return $this->featured; } public function getSlugOptions(): SlugOptions @@ -139,6 +138,35 @@ public function getMetaConfig(): MetaModelConfig return $this->metaConfig; } + protected ?LinkableConfig $_linkableConfig; + + public function linkableConfig(): ?LinkableConfig + { + $route = config('laravel-nova-news.front_routes_name.post'); + $routeParameterName = config('laravel-nova-news.front_routes_parameters.post'); + if (empty($routeParameterName) && empty($route)) { + return null; + } + + if (! isset($this->_linkableConfig)) { + $this->_linkableConfig = new LinkableConfig( + routeName: $route, + routeParameterName: $routeParameterName, + optionLabel: 'title', + optionGroup: trans('laravel-nova-news::crud-post.resource_label'), + resolveQuery: function (Builder|NewsPost $query) { + $query->where('locale', app()->currentLocale()); + }, + resolveNotPreviewQuery: function (Builder|NewsPost $query) { + $query->published(); + }, + previewTokenField: 'preview_token' + ); + } + + return $this->_linkableConfig; + } + public function localParent(): HasOne { return $this->hasOne(static::class, 'id', 'locale_parent_id'); diff --git a/src/Models/NewsTag.php b/src/Models/NewsTag.php index 493d93e..e9fe524 100644 --- a/src/Models/NewsTag.php +++ b/src/Models/NewsTag.php @@ -2,10 +2,14 @@ namespace Novius\LaravelNovaNews\Models; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; +use Novius\LaravelLinkable\Configs\LinkableConfig; +use Novius\LaravelLinkable\Traits\Linkable; use Novius\LaravelNovaNews\Database\Factories\NewsTagFactory; use Novius\LaravelNovaNews\NovaNews; use Novius\LaravelTranslatable\Traits\Translatable; @@ -24,10 +28,11 @@ * @property Carbon|null $updated_at * @property Carbon|null $deleted_at */ -class NewsTag extends ModelWithUrl +class NewsTag extends Model { use HasFactory; use HasSlug; + use Linkable; use SoftDeletes; use Translatable; @@ -53,14 +58,34 @@ protected static function booted() }); } - public function getFrontRouteName(): ?string + public function getRouteKeyName(): string { - return config('laravel-nova-news.front_routes_name.tag'); + return 'slug'; } - public function getFrontRouteParameter(): ?string + protected ?LinkableConfig $_linkableConfig; + + public function linkableConfig(): ?LinkableConfig { - return config('laravel-nova-news.front_routes_parameters.tag'); + $route = config('laravel-nova-news.front_routes_name.tag'); + $routeParameterName = config('laravel-nova-news.front_routes_parameters.tag'); + if (empty($routeParameterName) && empty($route)) { + return null; + } + + if (! isset($this->_linkableConfig)) { + $this->_linkableConfig = new LinkableConfig( + routeName: $route, + routeParameterName: $routeParameterName, + optionLabel: 'name', + optionGroup: trans('laravel-nova-news::crud-tag.resource_label'), + resolveQuery: function (Builder|NewsCategory $query) { + $query->where('locale', app()->currentLocale()); + }, + ); + } + + return $this->_linkableConfig; } public function getSlugOptions(): SlugOptions