feat(news): implement news resource with CRUD functionality
- Created NewsResource for managing news entries. - Added pages for creating, editing, and listing news. - Defined NewsForm schema for news entry forms. - Configured NewsTable for displaying news records with actions. - Implemented News model with relationships and soft deletes. - Added factories for generating test data for news and tags. - Created migrations for news and tags tables.
This commit is contained in:
parent
73daf8b80d
commit
2c5b057958
69
app/Filament/Resources/Publication/News/NewsResource.php
Normal file
69
app/Filament/Resources/Publication/News/NewsResource.php
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Publication\News;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Publication\News\Pages\CreateNews;
|
||||||
|
use App\Filament\Resources\Publication\News\Pages\EditNews;
|
||||||
|
use App\Filament\Resources\Publication\News\Pages\ListNews;
|
||||||
|
use App\Filament\Resources\Publication\News\Schemas\NewsForm;
|
||||||
|
use App\Filament\Resources\Publication\News\Tables\NewsTable;
|
||||||
|
use App\Models\News;
|
||||||
|
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 NewsResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = News::class;
|
||||||
|
|
||||||
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::Newspaper;
|
||||||
|
|
||||||
|
protected static ?string $navigationLabel = 'Berita';
|
||||||
|
|
||||||
|
protected static string|UnitEnum|null $navigationGroup = 'Publikasi';
|
||||||
|
|
||||||
|
protected static ?string $slug = 'publication/news';
|
||||||
|
|
||||||
|
protected static ?string $recordTitleAttribute = 'title';
|
||||||
|
|
||||||
|
protected static ?int $navigationSort = 1;
|
||||||
|
|
||||||
|
public static function form(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return NewsForm::configure($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return NewsTable::configure($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => ListNews::route('/'),
|
||||||
|
'create' => CreateNews::route('/create'),
|
||||||
|
'edit' => EditNews::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||||
|
{
|
||||||
|
return parent::getRecordRouteBindingEloquentQuery()
|
||||||
|
->withoutGlobalScopes([
|
||||||
|
SoftDeletingScope::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Filament/Resources/Publication/News/Pages/CreateNews.php
Normal file
38
app/Filament/Resources/Publication/News/Pages/CreateNews.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Publication\News\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Publication\News\NewsResource;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class CreateNews extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = NewsResource::class;
|
||||||
|
|
||||||
|
protected static ?string $title = 'Tambah Berita';
|
||||||
|
|
||||||
|
protected function getRedirectUrl(): string
|
||||||
|
{
|
||||||
|
return $this->getResource()::getUrl('index');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Action::make('back')
|
||||||
|
->label('Kembali')
|
||||||
|
->url(fn (): string => route('filament.dashboard.resources.publication.news.index'))
|
||||||
|
->outlined()
|
||||||
|
->color('secondary'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleRecordCreation(array $data): Model
|
||||||
|
{
|
||||||
|
$data['author_id'] = auth()->id();
|
||||||
|
|
||||||
|
return static::getModel()::create($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/Filament/Resources/Publication/News/Pages/EditNews.php
Normal file
28
app/Filament/Resources/Publication/News/Pages/EditNews.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Publication\News\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Publication\News\NewsResource;
|
||||||
|
use Filament\Actions\Action;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditNews extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = NewsResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Action::make('back')
|
||||||
|
->label('Kembali')
|
||||||
|
->url(fn (): string => route('filament.dashboard.resources.publication.news.index'))
|
||||||
|
->outlined()
|
||||||
|
->color('secondary'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRedirectUrl(): string
|
||||||
|
{
|
||||||
|
return $this->getResource()::getUrl('index');
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/Filament/Resources/Publication/News/Pages/ListNews.php
Normal file
22
app/Filament/Resources/Publication/News/Pages/ListNews.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Publication\News\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\Publication\News\NewsResource;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListNews extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = NewsResource::class;
|
||||||
|
|
||||||
|
protected static ?string $title = 'Berita';
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
CreateAction::make()
|
||||||
|
->label('Tambah'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/Filament/Resources/Publication/News/Schemas/NewsForm.php
Normal file
66
app/Filament/Resources/Publication/News/Schemas/NewsForm.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Publication\News\Schemas;
|
||||||
|
|
||||||
|
use Filament\Forms\Components\RichEditor;
|
||||||
|
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
|
||||||
|
use Filament\Forms\Components\TagsInput;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Schemas\Components\Section;
|
||||||
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
|
class NewsForm
|
||||||
|
{
|
||||||
|
public static function configure(Schema $schema): Schema
|
||||||
|
{
|
||||||
|
return $schema
|
||||||
|
->components([
|
||||||
|
Section::make([
|
||||||
|
TextInput::make('title')
|
||||||
|
->label('Judul')
|
||||||
|
->placeholder('Berita Purwakarta Hari Ini')
|
||||||
|
->autocomplete(false)
|
||||||
|
->autofocus()
|
||||||
|
->required()
|
||||||
|
->maxLength(200),
|
||||||
|
|
||||||
|
RichEditor::make('content')
|
||||||
|
->label('Konten')
|
||||||
|
->placeholder('...')
|
||||||
|
->required()
|
||||||
|
->columnSpanFull(),
|
||||||
|
|
||||||
|
TextInput::make('link')
|
||||||
|
->label('Tautan')
|
||||||
|
->placeholder('https://www.pwknews.com')
|
||||||
|
->autocomplete(false)
|
||||||
|
->nullable()
|
||||||
|
->maxLength(50)
|
||||||
|
->url(),
|
||||||
|
])->columnSpan(2),
|
||||||
|
|
||||||
|
Section::make([
|
||||||
|
TagsInput::make('tags')
|
||||||
|
->reactive()
|
||||||
|
->afterStateHydrated(function ($component, $state, $record) {
|
||||||
|
if (! $record) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$component->state(
|
||||||
|
$record->tags->pluck('name')->toArray()
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
|
||||||
|
SpatieMediaLibraryFileUpload::make('thumbnail')
|
||||||
|
->label('Thumbnail')
|
||||||
|
->disk(config('filesystems.default'))
|
||||||
|
->acceptedFileTypes(['images/*'])
|
||||||
|
->maxSize(1024 * 3)
|
||||||
|
->collection('news')
|
||||||
|
->image()
|
||||||
|
->required(),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->columns(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
78
app/Filament/Resources/Publication/News/Tables/NewsTable.php
Normal file
78
app/Filament/Resources/Publication/News/Tables/NewsTable.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\Publication\News\Tables;
|
||||||
|
|
||||||
|
use App\Filament\Actions\DefaultBulkActions;
|
||||||
|
use App\Filament\Columns\TimestampColumns;
|
||||||
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
use Filament\Actions\DeleteAction;
|
||||||
|
use Filament\Actions\EditAction;
|
||||||
|
use Filament\Actions\ForceDeleteAction;
|
||||||
|
use Filament\Actions\RestoreAction;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\TrashedFilter;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class NewsTable
|
||||||
|
{
|
||||||
|
public static function configure(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
TextColumn::make('title')
|
||||||
|
->label('Judul')
|
||||||
|
->searchable()
|
||||||
|
->sortable()
|
||||||
|
->wrap()
|
||||||
|
->limit(50),
|
||||||
|
|
||||||
|
TextColumn::make('author.name')
|
||||||
|
->label('Penulis')
|
||||||
|
->searchable()
|
||||||
|
->sortable()
|
||||||
|
->placeholder('Tidak ada'),
|
||||||
|
|
||||||
|
TextColumn::make('view')
|
||||||
|
->label('Dilihat')
|
||||||
|
->numeric()
|
||||||
|
->sortable()
|
||||||
|
->alignCenter(),
|
||||||
|
|
||||||
|
TextColumn::make('tags.name')
|
||||||
|
->label('Tag')
|
||||||
|
->listWithLineBreaks()
|
||||||
|
->limitList(3)
|
||||||
|
->expandableLimitedList()
|
||||||
|
->badge()
|
||||||
|
->placeholder('Tidak ada tag'),
|
||||||
|
|
||||||
|
TextColumn::make('link')
|
||||||
|
->label('Tautan')
|
||||||
|
->searchable()
|
||||||
|
->sortable()
|
||||||
|
->copyable()
|
||||||
|
->copyMessage('Tautan berhasil disalin')
|
||||||
|
->placeholder('Tidak ada'),
|
||||||
|
|
||||||
|
...TimestampColumns::make(),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
TrashedFilter::make()->native(false),
|
||||||
|
])
|
||||||
|
->recordActions([
|
||||||
|
EditAction::make(),
|
||||||
|
DeleteAction::make(),
|
||||||
|
ForceDeleteAction::make(),
|
||||||
|
RestoreAction::make(),
|
||||||
|
])
|
||||||
|
->toolbarActions([
|
||||||
|
BulkActionGroup::make([
|
||||||
|
...DefaultBulkActions::make('Berita'),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->emptyStateIcon('heroicon-o-newspaper')
|
||||||
|
->emptyStateDescription('Setelah Anda membuat data pertama, maka akan muncul disini.')
|
||||||
|
->defaultSort('created_at', 'desc')
|
||||||
|
->deferFilters(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
app/Models/News.php
Normal file
32
app/Models/News.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
use Spatie\Sluggable\HasSlug;
|
||||||
|
use Spatie\Sluggable\SlugOptions;
|
||||||
|
use Spatie\Tags\HasTags;
|
||||||
|
|
||||||
|
class News extends Model implements HasMedia
|
||||||
|
{
|
||||||
|
use HasFactory, HasSlug, HasTags, InteractsWithMedia, SoftDeletes;
|
||||||
|
|
||||||
|
protected $guarded = ['id'];
|
||||||
|
|
||||||
|
public function getSlugOptions(): SlugOptions
|
||||||
|
{
|
||||||
|
return SlugOptions::create()
|
||||||
|
->generateSlugsFrom('title')
|
||||||
|
->saveSlugsTo('slug');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function author(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'author_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
65
database/factories/NewsFactory.php
Normal file
65
database/factories/NewsFactory.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\News;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\News>
|
||||||
|
*/
|
||||||
|
class NewsFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = News::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$title = $this->faker->sentence(6);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'author_id' => User::factory(),
|
||||||
|
'title' => $title,
|
||||||
|
'slug' => Str::slug($title),
|
||||||
|
'content' => $this->faker->paragraphs(3, true),
|
||||||
|
'link' => $this->faker->optional(0.3)->url(),
|
||||||
|
'view' => $this->faker->numberBetween(0, 10000),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the news has high views.
|
||||||
|
*/
|
||||||
|
public function popular(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'view' => $this->faker->numberBetween(5000, 50000),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the news has external link.
|
||||||
|
*/
|
||||||
|
public function withLink(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'link' => $this->faker->url(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the news has no views.
|
||||||
|
*/
|
||||||
|
public function unpublished(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'view' => 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
81
database/factories/NewsTagsFactory.php
Normal file
81
database/factories/NewsTagsFactory.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\News;
|
||||||
|
use App\Models\NewsTags;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\NewsTags>
|
||||||
|
*/
|
||||||
|
class NewsTagsFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = NewsTags::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$tags = [
|
||||||
|
'Berita',
|
||||||
|
'Update',
|
||||||
|
'Pengumuman',
|
||||||
|
'Event',
|
||||||
|
'Kegiatan',
|
||||||
|
'Kominfo',
|
||||||
|
'Diskominfo',
|
||||||
|
'Pemerintah',
|
||||||
|
'Publik',
|
||||||
|
'Informasi',
|
||||||
|
'Layanan',
|
||||||
|
'Digital',
|
||||||
|
'Teknologi',
|
||||||
|
'Sosial',
|
||||||
|
'Ekonomi',
|
||||||
|
'Pendidikan',
|
||||||
|
'Kesehatan',
|
||||||
|
'Lingkungan',
|
||||||
|
'Infrastruktur',
|
||||||
|
'Transportasi',
|
||||||
|
];
|
||||||
|
|
||||||
|
return [
|
||||||
|
'news_id' => News::factory(),
|
||||||
|
'name' => $this->faker->randomElement($tags),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the tag is for news category.
|
||||||
|
*/
|
||||||
|
public function news(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'name' => 'Berita',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the tag is for announcement category.
|
||||||
|
*/
|
||||||
|
public function announcement(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'name' => 'Pengumuman',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the tag is for event category.
|
||||||
|
*/
|
||||||
|
public function event(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'name' => 'Event',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
database/migrations/2025_12_12_082036_create_news_table.php
Normal file
36
database/migrations/2025_12_12_082036_create_news_table.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('news', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignIdFor(User::class, 'author_id');
|
||||||
|
$table->string('title', 200);
|
||||||
|
$table->string('slug', 200);
|
||||||
|
$table->text('content');
|
||||||
|
$table->string('link', 50)->nullable();
|
||||||
|
$table->unsignedInteger('view')->default(0);
|
||||||
|
$table->timestamp('created_at')->useCurrent();
|
||||||
|
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||||
|
$table->softDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('news');
|
||||||
|
}
|
||||||
|
};
|
||||||
36
database/migrations/2025_12_12_085015_create_tag_tables.php
Normal file
36
database/migrations/2025_12_12_085015_create_tag_tables.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('tags', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->json('name');
|
||||||
|
$table->json('slug');
|
||||||
|
$table->string('type')->nullable();
|
||||||
|
$table->integer('order_column')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('taggables', function (Blueprint $table) {
|
||||||
|
$table->foreignId('tag_id')->constrained()->cascadeOnDelete();
|
||||||
|
|
||||||
|
$table->morphs('taggable');
|
||||||
|
|
||||||
|
$table->unique(['tag_id', 'taggable_id', 'taggable_type']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('taggables');
|
||||||
|
Schema::dropIfExists('tags');
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user