Laravel Model Olaylarında (created, saved) İlişkili Veriler Neden Boş Geliyor?
- Düzenlendi
mgsmus Kuyruk kullanmıyorum, paylaşımlı sunucuda barındırıyorum web siteyi. Kullanma imkanı yok
- Düzenlendi
Post
class Post extends Model implements HasMedia
{
use HasFactory;
use InteractsWithMedia;
use Searchable;
public function toSearchableArray()
{
return [
'title' => $this->title,
];
}
protected static function boot()
{
parent::boot();
// Varsayılan olarak ilişkili verileri yükle
static::addGlobalScope('eagerLoadRelations', function ($query) {
$query->with(['media', 'categories']);
});
static::saved(function ($post) {
$freshPost = Post::with(['media', 'categories'])->find($post->id);
\Log::info('Post created.', [
'post_id' => $freshPost->id,
'media' => $freshPost->media,
'categories' => $freshPost->categories
]);
});
static::creating(function ($post) {
$post->sort_order = static::max('sort_order') + 1;
});
}
protected $fillable = [
'title',
'body',
'status',
'sort_order',
'author_id',
'mp3',
'created_at',
'updated_at',
];
protected $casts = [
'id' => 'integer',
'status' => 'boolean',
'sort_order' => 'integer',
'author_id' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public function author(): BelongsTo
{
return $this->belongsTo(Author::class, 'author_id');
}
public function getFormattedCreatedAtAttribute()
{
Carbon::setLocale('tr'); // Türkçe locale ayarı
return $this->created_at->translatedFormat('d F Y'); // Örneğin: 15 Haziran 2018
}
public function getPostUrl(): string
{
return url('/haber/'.Str::slug($this->title).'/'.$this->id);
}
public function getPostImage(): ?string
{
$mediaItem = $this->getFirstMedia('post');
return $mediaItem ? $mediaItem->getUrl() : null;
}
public function categories()
{
return $this->belongsToMany(
Category::class,
'category_post',
'post_id',
'category_id'
);
}
public function tags()
{
return $this->belongsToMany(Tag::class, 'post_tag', 'post_id', 'tag_id');
}
}
class Category extends Model
{
use HasFactory;
protected static function boot()
{
parent::boot();
static::creating(function ($category) {
$category->sort_order = static::max('sort_order') + 1;
});
}
protected $fillable = [
'name',
'sort_order',
'status',
];
protected $casts = [
'id' => 'integer',
'sort_order' => 'integer',
'status' => 'boolean',
];
public function posts()
{
return $this->belongsToMany(
Post::class,
'category_post',
'category_id',
'post_id'
);
}
}
- Düzenlendi
mgsmus Filamentphp kullanıyorum resource tetikliyor
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\PostResource\Pages;
use App\Models\Category;
use App\Models\Post;
use App\Models\Tag;
use Filament\Forms;
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
/**
* @property \App\Models\Post $record
*/
class PostResource extends Resource
{
protected static ?string $model = Post::class;
protected static ?string $navigationIcon = 'gmdi-post-add-r';
protected static ?string $navigationLabel = 'Haberler';
protected static ?string $pluralLabel = 'Haberler';
public static function form(Form $form): Form
{
return $form
->columns(4)
->schema([
Forms\Components\Section::make([
Forms\Components\TextInput::make('title')
->required()
->columnSpan(2)
->maxLength(255),
Forms\Components\Select::make('author_id')
->relationship('author', 'name')
->default(1)
->columnSpan(2)
->required(),
])->columns(4),
Forms\Components\Section::make([
Forms\Components\Select::make('categories')
->multiple()
->relationship('categories', 'name')
->options(Category::pluck('name', 'id'))
->searchable()
->required(),
Forms\Components\Select::make('tag')
->multiple()
->relationship('tags', 'name')
->options(Tag::pluck('name', 'id'))
->createOptionForm([
Forms\Components\TextInput::make('name')
->required(),
])
->preload() // Optionally preload existing tags for faster selection
->searchable() // Allow searching tags
->required(),
Forms\Components\DatePicker::make('created_at')
->native(false)
->displayFormat('d F Y')
->required(),
])->columns(3),
SpatieMediaLibraryFileUpload::make('post')->label('Baş Resim')
->image()
->columnSpanFull()
->imageEditor()
->collection('post')
->disk('custom_public'), // Bu satırı ekledik
Forms\Components\FileUpload::make('mp3')
->disk('custom_public')
->acceptedFileTypes(['audio/mpeg']),
Forms\Components\RichEditor::make('body')
->required()
->fileAttachmentsDisk('custom_public')
->columnSpanFull(),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('title')
->searchable(),
Tables\Columns\IconColumn::make('status')
->boolean(),
Tables\Columns\TextColumn::make('sort_order')
->numeric()
->sortable(),
Tables\Columns\TextColumn::make('author.name')
->numeric()
->sortable(),
Tables\Columns\TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->actions([
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListPosts::route('/'),
'create' => Pages\CreatePost::route('/create'),
'view' => Pages\ViewPost::route('/{record}'),
'edit' => Pages\EditPost::route('/{record}/edit'),
];
}
}
- Düzenlendi
macintyre Anladım sanırım, sizinle ilgili değil. saved/created olayı model kaydedildikten sonra tetikleniyor. İlişkiler için ise model id gerektiği için sonradan oluşturuluyor. O yüzden saved sırasında sadece modeli dinleyebiliyorsunuz. İlişki saved olayından sonra gerçekleşiyor. Yani işlem sırası
- Post
- Post creating/saving
- Post created/saved
- Post retrieved (Eğer Post modeline erişmek için tekrar sorgu yapılıyorsa)
- Category
- Category creating/saving
- Category created/saved
şeklinde gerçekleşiyor. Bunun için model event değil normal event/listener kurgusu kullanmanız lazım. Filamentphp kullanmadığım için onda nasıl yapılır bilmiyorum.
Filament kullanıyorsanız filament'in Hooklarını kullansanız daha sağlıklı olur ve yakalanması daha kolay olur diye düşünüyorum.
https://filamentphp.com/docs/2.x/admin/resources/editing-records#lifecycle-hooks
Örnek olarak
protected function beforeSave(): void
{
if (! $this->record->team->subscribed()) {
Notification::make()
->warning()
->title('You don\'t have an active subscription!')
->body('Choose a plan to continue.')
->persistent()
->actions([
Action::make('subscribe')
->button()
->url(route('subscribe'), shouldOpenInNewTab: true),
])
->send();
$this->halt();
}
}
- Düzenlendi
koti42 Teşekkür ederim, ilgilendiğiniz için
https://filamentphp.com/docs/3.x/actions/prebuilt-actions/create#lifecycle-hooks
- sürümdeki şu dökümanı kullandım ve işe yaramadı log dahi tetiklenmiyor. ilginç
class ListPosts extends ListRecords
{
protected static string $resource = PostResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make()
->after(function (Actions\CreateAction $action, Post $record) {
Log::info("CreateAction after fn started");
event(new CachePostsEvent());
Log::info("CreateAction after fn completed");
}),
];
}
}
mgsmus Bunun için model event değil normal event/listener kurgusu kullanmanız lazım.
Mustafa Bey'in tavsiyesi ile event-listener ikilisi oluşturdum ve bunu Post modelin saved olayında tetikledim.
Post Model
static::saved(function ($post) {
try {
$i = 1;
while (Cache::has("posts-page-$i")) {
Cache::forget("posts-page-$i");
$i++;
}
Log::info("Pages Cache cleared");
event(new CachePostsEvent(postId: $post->id));
} catch (\Throwable $th) {
Log::error('Error clearing cache for posts-page-: ' . $th->getMessage(), ['exception' => $th]);
}
});
Listener
public function handle(CachePostsEvent $event): void
{
try {
$post_id = $event->postId;
$post = Post::with('categories', 'media')->find($post_id);
Log::info("Post ", ["Post" => $post]);
} catch (\Throwable $th) {
Log::error("Error sending GET requests to all pages: " . $th->getMessage(), [
"exception" => $th
]);
}
}
Bu sayede hedefime ulaştım çok çok teşekkürler.
- Düzenlendi
Selam, bir diğer yöntem Observer da çözülebilir. Genelde ben bu şekilde kullanıyorum.
- Düzenlendi
Funal Selam, Observer sınıfı olay ve dinleyici yapısını tek bir dosya içerisine topluyor. Depo (Repository) tasarım kalıbı gibi. Çalıştırdığı olay boot()
içerisindeki static::
ile başlayan olaylar yine, bu yüzden bir farklılık yaratmıyor.
https://laravel.com/docs/12.x/eloquent#observers
Observers
Defining ObserversIf you are listening for many events on a given model, you may use observers to group all of your listeners into a single class.