Add Blog Category Resource and CRUD Pages: Created BlogCategoryResource with associated pages for listing, creating, and editing blog categories. Implemented localization for all labels and messages. Enhanced BlogCategory model to specify foreign key for related blogs.

This commit is contained in:
Ümit Tunç
2025-11-06 17:48:20 -03:00
parent 7563577aa8
commit 68fd923b42
9 changed files with 462 additions and 1 deletions
@@ -0,0 +1,77 @@
<?php
namespace App\Filament\Admin\Resources\BlogCategories;
use App\Filament\Admin\Resources\BlogCategories\Pages\CreateBlogCategory;
use App\Filament\Admin\Resources\BlogCategories\Pages\EditBlogCategory;
use App\Filament\Admin\Resources\BlogCategories\Pages\ListBlogCategories;
use App\Filament\Admin\Resources\BlogCategories\Schemas\BlogCategoryForm;
use App\Filament\Admin\Resources\BlogCategories\Tables\BlogCategoriesTable;
use App\Models\BlogCategory;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use UnitEnum;
class BlogCategoryResource extends Resource
{
protected static ?string $model = BlogCategory::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedTag;
protected static UnitEnum|string|null $navigationGroup = 'Blog';
public static function getNavigationLabel(): string
{
return __('blog_category.navigation_label');
}
public static function getModelLabel(): string
{
return __('blog_category.model_label');
}
public static function getPluralModelLabel(): string
{
return __('blog_category.plural_model_label');
}
public static function form(Schema $schema): Schema
{
return BlogCategoryForm::configure($schema);
}
public static function table(Table $table): Table
{
return BlogCategoriesTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListBlogCategories::route('/'),
'create' => CreateBlogCategory::route('/create'),
'edit' => EditBlogCategory::route('/{record}/edit'),
];
}
public static function getRecordRouteBindingEloquentQuery(): Builder
{
return parent::getRecordRouteBindingEloquentQuery()
->withoutGlobalScopes([
SoftDeletingScope::class,
]);
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Filament\Admin\Resources\BlogCategories\Pages;
use App\Filament\Admin\Resources\BlogCategories\BlogCategoryResource;
use Filament\Resources\Pages\CreateRecord;
class CreateBlogCategory extends CreateRecord
{
protected static string $resource = BlogCategoryResource::class;
public function getTitle(): string
{
return __('blog_category.create');
}
protected function getRedirectUrl(): string
{
return $this->getResource()::getUrl('index');
}
protected function getCreatedNotificationTitle(): ?string
{
return __('blog_category.created_successfully');
}
}
@@ -0,0 +1,62 @@
<?php
namespace App\Filament\Admin\Resources\BlogCategories\Pages;
use App\Filament\Admin\Resources\BlogCategories\BlogCategoryResource;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction;
use Filament\Resources\Pages\EditRecord;
class EditBlogCategory extends EditRecord
{
protected static string $resource = BlogCategoryResource::class;
public function getTitle(): string
{
return __('blog_category.edit');
}
protected function getHeaderActions(): array
{
return [
Action::make('save')
->label(__('blog_category.save'))
->action('save')
->keyBindings(['mod+s'])
->color('primary')
->size('sm'),
Action::make('cancel')
->label(__('blog_category.cancel'))
->url($this->getResource()::getUrl('index'))
->color('gray')
->size('sm'),
DeleteAction::make()
->label(__('blog_category.delete'))
->size('sm'),
RestoreAction::make()
->label(__('blog_category.restore'))
->size('sm'),
ForceDeleteAction::make()
->label(__('blog_category.force_delete'))
->size('sm'),
];
}
protected function getFormActions(): array
{
return []; // Boş array döndürerek alt taraftaki form action'larını gizliyoruz
}
protected function getRedirectUrl(): string
{
return $this->getResource()::getUrl('index');
}
protected function getSavedNotificationTitle(): ?string
{
return __('blog_category.updated_successfully');
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Filament\Admin\Resources\BlogCategories\Pages;
use App\Filament\Admin\Resources\BlogCategories\BlogCategoryResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListBlogCategories extends ListRecords
{
protected static string $resource = BlogCategoryResource::class;
public function getTitle(): string
{
return __('blog_category.title');
}
protected function getHeaderActions(): array
{
return [
CreateAction::make()
->label(__('blog_category.create')),
];
}
}
@@ -0,0 +1,70 @@
<?php
namespace App\Filament\Admin\Resources\BlogCategories\Schemas;
use Filament\Forms\Components\ColorPicker;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
class BlogCategoryForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->columns(2)
->schema([
Section::make(__('blog_category.general_section'))
->schema([
TextInput::make('name')
->label(__('blog_category.name_field'))
->required()
->maxLength(255)
->live(onBlur: true)
->afterStateUpdated(function (string $operation, $state, callable $set) {
if ($operation !== 'create') {
return;
}
$set('slug', \Str::slug($state));
}),
TextInput::make('slug')
->label(__('blog_category.slug_field'))
->required()
->maxLength(255)
->unique(ignoreRecord: true)
->rules(['alpha_dash'])
->helperText(__('blog_category.slug_helper')),
Textarea::make('description')
->label(__('blog_category.description_field'))
->rows(3)
->maxLength(1000)
->helperText(__('blog_category.description_helper'))
->columnSpanFull(),
ColorPicker::make('color')
->label(__('blog_category.color_field'))
->default('#3B82F6')
->helperText(__('blog_category.color_helper')),
TextInput::make('sort_order')
->label(__('blog_category.sort_order_field'))
->numeric()
->default(0)
->helperText(__('blog_category.sort_order_helper')),
Toggle::make('is_active')
->label(__('blog_category.is_active_field'))
->default(true)
->helperText(__('blog_category.is_active_helper'))
->columnSpanFull(),
])
->columnSpanFull()
->collapsible(false),
]);
}
}
@@ -0,0 +1,93 @@
<?php
namespace App\Filament\Admin\Resources\BlogCategories\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Tables\Columns\ColorColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
class BlogCategoriesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
ColorColumn::make('color')
->label(__('blog_category.color_field')),
TextColumn::make('name')
->label(__('blog_category.name_field'))
->searchable()
->sortable()
->weight('bold'),
TextColumn::make('slug')
->label(__('blog_category.slug_field'))
->searchable()
->sortable()
->limit(30)
->color('gray'),
TextColumn::make('description')
->label(__('blog_category.description_field'))
->limit(50)
->wrap()
->toggleable(),
TextColumn::make('blogs_count')
->label(__('blog_category.blogs_count'))
->counts('blogs')
->sortable()
->alignCenter(),
TextColumn::make('sort_order')
->label(__('blog_category.sort_order_field'))
->sortable()
->alignCenter()
->toggleable(),
ToggleColumn::make('is_active')
->label(__('blog_category.is_active_field'))
->alignCenter(),
TextColumn::make('created_at')
->label(__('blog_category.table_created_at'))
->dateTime('d.m.Y H:i')
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->label(__('blog_category.table_updated_at'))
->dateTime('d.m.Y H:i')
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
TernaryFilter::make('is_active')
->label(__('blog_category.is_active_field')),
])
->recordActions([
EditAction::make()
->label(__('blog_category.edit')),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make()
->label(__('blog_category.delete')),
RestoreBulkAction::make()
->label(__('blog_category.restore')),
ForceDeleteBulkAction::make()
->label(__('blog_category.force_delete')),
]),
])
->defaultSort('sort_order');
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ class BlogCategory extends Model
public function blogs()
{
return $this->hasMany(Blog::class);
return $this->hasMany(Blog::class, 'category_id');
}
public function getRouteKeyName()
+53
View File
@@ -0,0 +1,53 @@
<?php
return [
// Module labels
'title' => 'Blog Categories',
'navigation_label' => 'Blog Categories',
'model_label' => 'Blog Category',
'plural_model_label' => 'Blog Categories',
// Actions
'create' => 'Create New Blog Category',
'edit' => 'Edit Blog Category',
'save' => 'Save',
'cancel' => 'Cancel',
'delete' => 'Delete Blog Category',
'restore' => 'Restore Blog Category',
'force_delete' => 'Force Delete',
// Sections
'general_section' => 'General Information',
// Form fields
'name_field' => 'Category Name',
'slug_field' => 'URL Slug',
'description_field' => 'Description',
'color_field' => 'Color',
'sort_order_field' => 'Sort Order',
'is_active_field' => 'Active',
// Helper texts
'slug_helper' => 'The part that will appear in the URL. Example: technology',
'description_helper' => 'Category description (optional)',
'color_helper' => 'Select category color (hex format)',
'sort_order_helper' => 'Category listing order (smaller numbers appear first)',
'is_active_helper' => 'Activate/deactivate category',
// Messages
'created_successfully' => 'Blog category created successfully.',
'updated_successfully' => 'Blog category updated successfully.',
'deleted_successfully' => 'Blog category deleted successfully.',
'restored_successfully' => 'Blog category restored successfully.',
// Table columns
'blogs_count' => 'Blog Count',
'table_created_at' => 'Created At',
'table_updated_at' => 'Updated At',
// Validation messages
'name_required' => 'Category name field is required.',
'slug_required' => 'URL slug field is required.',
'slug_unique' => 'This URL slug is already in use.',
];
+53
View File
@@ -0,0 +1,53 @@
<?php
return [
// Module labels
'title' => 'Blog Kategorileri',
'navigation_label' => 'Blog Kategorileri',
'model_label' => 'Blog Kategorisi',
'plural_model_label' => 'Blog Kategorileri',
// Actions
'create' => 'Yeni Blog Kategorisi Oluştur',
'edit' => 'Blog Kategorisini Düzenle',
'save' => 'Kaydet',
'cancel' => 'İptal',
'delete' => 'Blog Kategorisini Sil',
'restore' => 'Blog Kategorisini Geri Yükle',
'force_delete' => 'Kalıcı Olarak Sil',
// Sections
'general_section' => 'Genel Bilgiler',
// Form fields
'name_field' => 'Kategori Adı',
'slug_field' => 'URL Yolu',
'description_field' => 'Açıklama',
'color_field' => 'Renk',
'sort_order_field' => 'Sıralama',
'is_active_field' => 'Aktif',
// Helper texts
'slug_helper' => 'URL\'de görünecek kısım. Örnek: teknoloji',
'description_helper' => 'Kategori açıklaması (isteğe bağlı)',
'color_helper' => 'Kategori rengini seçin (hex formatında)',
'sort_order_helper' => 'Kategorilerin listelenme sırası (küçük sayılar önce gelir)',
'is_active_helper' => 'Kategoriyi aktif/pasif yap',
// Messages
'created_successfully' => 'Blog kategorisi başarıyla oluşturuldu.',
'updated_successfully' => 'Blog kategorisi başarıyla güncellendi.',
'deleted_successfully' => 'Blog kategorisi başarıyla silindi.',
'restored_successfully' => 'Blog kategorisi başarıyla geri yüklendi.',
// Table columns
'blogs_count' => 'Blog Sayısı',
'table_created_at' => 'Oluşturulma Tarihi',
'table_updated_at' => 'Güncellenme Tarihi',
// Validation messages
'name_required' => 'Kategori adı alanı zorunludur.',
'slug_required' => 'URL yolu alanı zorunludur.',
'slug_unique' => 'Bu URL yolu zaten kullanılıyor.',
];