From 7ed709799a02a25e13b1828f02ac6c281a3ba7ed Mon Sep 17 00:00:00 2001 From: Krishanu <36711704+krishzzi@users.noreply.github.com> Date: Fri, 18 Oct 2024 18:37:49 +0530 Subject: [PATCH] added docs and optimized --- src/Actions/SpatieMediaAction.php | 70 ++++++++++++++---------- src/TipTapMedia.php | 72 +++++++++++++------------ src/Traits/HasMediaActionFormSchema.php | 65 +++++++++++----------- src/Traits/HasMediaActionSupport.php | 42 +++++++++------ 4 files changed, 140 insertions(+), 109 deletions(-) diff --git a/src/Actions/SpatieMediaAction.php b/src/Actions/SpatieMediaAction.php index c9a0c8d..b372619 100644 --- a/src/Actions/SpatieMediaAction.php +++ b/src/Actions/SpatieMediaAction.php @@ -9,58 +9,74 @@ use Filament\Forms\Components\Actions\Action; use FilamentTiptapEditor\TiptapEditor; +/** + * Class SpatieMediaAction + * + * Handles media-related actions within the Tiptap editor using Spatie's media handling. + * This action enables users to insert media such as images or videos into the editor. + */ class SpatieMediaAction extends Action { + use HasMediaActionSupport, HasMediaActionFormSchema; - use HasMediaActionSupport,HasMediaActionFormSchema; - + /** + * Constant defining the column layout for the form schema. + */ public const FORM_COLUMN = 5; + /** + * Provides the default action name. + * + * @return string|null The default name of the action. + */ public static function getDefaultName(): ?string { return 'filament_tiptap_media'; } - - - - + /** + * Set up the action with view, arguments, and other form settings. + */ protected function setUp(): void { parent::setUp(); $this - ->view('asdf') - ->arguments(TipTapMedia::getTipTapEditorDefaultArguments()) - ->modalWidth('fit') - ->slideOver() - ->form(fn(TiptapEditor $component,ComponentContainer $form) => $this->getFormSchema($component,$form)) - ->mountUsing(fn (TiptapEditor $component, ComponentContainer $form, array $arguments) => $this->getMountWith($component,$form,$arguments)) - ->modalHeading(fn (array $arguments) => 'Media Manager') - ->action(fn(TiptapEditor $component, array $data) => $this->handleTipTapMediaAction($component,$data)) - - ; + ->view('asdf') // View to be rendered, should be replaced with the actual view path + ->arguments(TipTapMedia::getTipTapEditorDefaultArguments()) // Default arguments for TipTap editor + ->modalWidth('fit') // Modal width setting + ->slideOver() // Modal behavior + ->form(fn (TiptapEditor $component, ComponentContainer $form) => $this->getFormSchema($component, $form)) // Form schema definition + ->mountUsing(fn (TiptapEditor $component, ComponentContainer $form, array $arguments) => $this->getMountWith($component, $form, $arguments)) // Mount form with provided arguments + ->modalHeading(fn (array $arguments) => 'Media Manager') // Modal heading definition + ->action(fn(TiptapEditor $component, array $data) => $this->handleTipTapMediaAction($component, $data)); // Action handling for media insertion } + /** + * Handles the action of inserting media into the editor. + * + * @param TiptapEditor $component The editor component instance. + * @param array $data The media data collected from the form. + */ protected function handleTipTapMediaAction(TiptapEditor $component, array $data): void { + // Clean the source URL before saving $source = $this->getCleanSourceOnSave($data); + // Dispatch the media insertion event to the Livewire component $component->getLivewire()->dispatch( event: 'insertFromAction', type: 'media', statePath: $component->getStatePath(), media: [ - 'src' => $source, - 'alt' => $data['alt'] ?? null, - 'title' => $data['title'], - 'width' => $data['width'], - 'height' => $data['height'], - 'lazy' => $data['lazy'] ?? false, - 'link_text' => $data['link_text'] ?? null, - ], + 'src' => $source, // Source URL of the media + 'alt' => $data['alt'] ?? null, // Alt text for the media + 'title' => $data['title'], // Title for the media + 'width' => $data['width'], // Width of the media + 'height' => $data['height'], // Height of the media + 'lazy' => $data['lazy'] ?? false, // Lazy loading flag + 'link_text' => $data['link_text'] ?? null, // Link text for the media, if applicable + ] ); } - - -} \ No newline at end of file +} diff --git a/src/TipTapMedia.php b/src/TipTapMedia.php index b0da60f..d296a18 100644 --- a/src/TipTapMedia.php +++ b/src/TipTapMedia.php @@ -3,15 +3,17 @@ namespace FilamentTiptapEditor; use FilamentTiptapEditor\TiptapEditor; - use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; class TipTapMedia { - - + /** + * Get the default arguments for the TipTap editor. + * + * @return array + */ public static function getTipTapEditorDefaultArguments(): array { return [ @@ -24,74 +26,78 @@ public static function getTipTapEditorDefaultArguments(): array ]; } - /** + * Get the media collection name based on the model or editor. + * * @param Model|TiptapEditor|null $component * @return string */ public static function mediaCollection(null|Model|TiptapEditor $component = null): string { - if ($component instanceof TiptapEditor) - { + if ($component instanceof TiptapEditor) { return Str::afterLast($component->getModel(), '\\') . 'TipTapMedia'; - }elseif($component instanceof Model) - { + } elseif ($component instanceof Model) { return Str::afterLast(get_class($component), '\\') . 'TipTapMedia'; - }else{ + } else { return 'TipTapMedia'; } - } - /** - * @param Model $record - * @param array $columns + * Handle media creation and update image URLs in the given columns. + * + * @param Model $record The Eloquent model instance. + * @param array $columns The columns to check for images. * @return void */ - public static function OnCreated(Model $record,array $columns): void + public static function OnCreated(Model $record, array $columns): void { - foreach ($columns as $column) - { + foreach ($columns as $column) { + // Find all images in the content preg_match_all('@/]*/?>@Ui', $record->{$column}, $allPreviousMatchedImages); $images = $allPreviousMatchedImages[1]; - foreach ($images as $image) - { - $cleanImagePath = Storage::path('public'.Str::remove(config('app.url').'/storage',$image)); + foreach ($images as $image) { + $cleanImagePath = Storage::path('public' . Str::remove(config('app.url') . '/storage', $image)); + // Add media to the collection $spatieMedia = $record->addMedia($cleanImagePath)->toMediaCollection(self::mediaCollection($record)); $newUrl = $spatieMedia->getUrl(); - // Update Content - $record->{$column} = Str::replace($image,$newUrl,$record->{$column}); - + // Update the content with the new media URL + $record->{$column} = Str::replace($image, $newUrl, $record->{$column}); } } $record->save(); } - - public static function OnSaved(Model $record,array $columns): void + /** + * Handle media updates after saving the record. + * + * @param Model $record The Eloquent model instance. + * @param array $columns The columns to check for images. + * @return void + */ + public static function OnSaved(Model $record, array $columns): void { $record->load([ - 'media' => fn($query) => $query->where('collection_name',self::mediaCollection($record)) + 'media' => fn ($query) => $query->where('collection_name', self::mediaCollection($record)), ]); + // Create a map of UUIDs to media URLs $spatieMediaList = $record->media->mapWithKeys(function ($media) { return [$media->uuid => $media->getUrl()]; })->toArray(); - foreach ($columns as $column) - { + foreach ($columns as $column) { + // Find all images in the content preg_match_all('@/]*/?>@Ui', $record->{$column}, $allPreviousMatchedImages); $images = $allPreviousMatchedImages[1]; - $deletable = array_diff($spatieMediaList,$images); - if (!empty($deletable)) - { - $deletableSpatieRecords = $record->media->whereIn('uuid',array_keys($deletable)); + + // Determine the deletable media (not present in the content anymore) + $deletable = array_diff($spatieMediaList, $images); + if (!empty($deletable)) { + $deletableSpatieRecords = $record->media->whereIn('uuid', array_keys($deletable)); $deletableSpatieRecords->each->delete(); } } } - - } diff --git a/src/Traits/HasMediaActionFormSchema.php b/src/Traits/HasMediaActionFormSchema.php index 8df149b..343a68a 100644 --- a/src/Traits/HasMediaActionFormSchema.php +++ b/src/Traits/HasMediaActionFormSchema.php @@ -2,7 +2,6 @@ namespace FilamentTiptapEditor\Traits; - use Filament\Forms\ComponentContainer; use Filament\Forms\Components\Actions\Action; use Filament\Forms\Components\BaseFileUpload; @@ -21,25 +20,25 @@ trait HasMediaActionFormSchema { - - /** - * @param TiptapEditor $component - * @param ComponentContainer $form - * @return array + * Get the form schema for the media action. + * + * @param TiptapEditor $component The editor component. + * @param ComponentContainer $form The form container. + * @return array The form schema. */ - protected function getFormSchema(TiptapEditor $component,ComponentContainer $form): array + protected function getFormSchema(TiptapEditor $component, ComponentContainer $form): array { return [ - Grid::make([ - 'md' => 1 - ])->schema(array_merge($this->getFileUploadFieldSchema($component),$this->getDefaultTipTapFormSchema())) + Grid::make(['md' => 1]) + ->schema(array_merge($this->getFileUploadFieldSchema($component), $this->getDefaultTipTapFormSchema())) ]; } - /** - * @return array + * Get the default form schema for TipTap media modal. + * + * @return array The default form schema. */ public function getDefaultTipTapFormSchema(): array { @@ -66,15 +65,15 @@ public function getDefaultTipTapFormSchema(): array TextInput::make('width'), TextInput::make('height'), ])->columns(), - Hidden::make('type') - ->default('document'), + Hidden::make('type')->default('document'), ]; } - /** - * @param TiptapEditor $component - * @return array + * Get the file upload field schema for media action. + * + * @param TiptapEditor $component The editor component. + * @return array The file upload field schema. */ public function getFileUploadFieldSchema(TiptapEditor $component): array { @@ -102,23 +101,25 @@ public function getFileUploadFieldSchema(TiptapEditor $component): array } }) ->saveUploadedFileUsing(static function (BaseFileUpload $component, TemporaryUploadedFile $file, ?Model $record) { - return is_null($record) ? self::OnCreate($component,$file,$record) : self::OnUpdate($component,$file,$record); + return is_null($record) ? self::OnCreate($component, $file, $record) : self::OnUpdate($component, $file, $record); }) ]; } - /** - * @param BaseFileUpload $component - * @param TemporaryUploadedFile $file - * @param Model|null $record - * @return mixed + * Handle file update for the media action. + * + * @param BaseFileUpload $component The file upload component. + * @param TemporaryUploadedFile $file The uploaded file. + * @param Model|null $record The model instance. + * @return mixed The URL of the updated media. */ protected static function OnUpdate(BaseFileUpload $component, TemporaryUploadedFile $file, ?Model $record) { $filename = $component->shouldPreserveFilenames() ? pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME) : Str::uuid(); $extension = $file->getClientOriginalExtension(); $filename = $filename . '-' . time() . '.' . $extension; + $mediaInstance = $record->addMedia($file) ->usingFileName($filename) ->toMediaCollection(TipTapMedia::mediaCollection($record)); @@ -126,18 +127,20 @@ protected static function OnUpdate(BaseFileUpload $component, TemporaryUploadedF return $mediaInstance->getUrl(); } - /** - * @param BaseFileUpload $component - * @param TemporaryUploadedFile $file - * @param Model|null $record - * @return mixed + * Handle file creation for the media action. + * + * @param BaseFileUpload $component The file upload component. + * @param TemporaryUploadedFile $file The uploaded file. + * @param Model|null $record The model instance. + * @return mixed The URL of the newly uploaded file. */ protected static function OnCreate(BaseFileUpload $component, TemporaryUploadedFile $file, ?Model $record) { $filename = $component->shouldPreserveFilenames() ? pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME) : Str::uuid(); $storeMethod = $component->getVisibility() === 'public' ? 'storePubliclyAs' : 'storeAs'; $extension = $file->getClientOriginalExtension(); + if (Storage::disk($component->getDiskName())->exists(ltrim($component->getDirectory() . '/' . $filename . '.' . $extension, '/'))) { $filename = $filename . '-' . time(); } @@ -146,10 +149,4 @@ protected static function OnCreate(BaseFileUpload $component, TemporaryUploadedF return Storage::disk($component->getDiskName())->url($upload); } - - - - - - } diff --git a/src/Traits/HasMediaActionSupport.php b/src/Traits/HasMediaActionSupport.php index cd3cbb2..e562c78 100644 --- a/src/Traits/HasMediaActionSupport.php +++ b/src/Traits/HasMediaActionSupport.php @@ -9,20 +9,29 @@ trait HasMediaActionSupport { - - - protected function getCleanSource(TiptapEditor $component,?string $source=null):?string + /** + * Cleans the source URL by removing the unnecessary parts of the path and returning the media path relative to storage. + * + * @param TiptapEditor $component The editor component. + * @param string|null $source The source URL. + * @return string|null The cleaned source path. + */ + protected function getCleanSource(TiptapEditor $component, ?string $source = null): ?string { - $source = $source !== '' - ? $component->getDirectory() . Str::of($source) - ->after($component->getDirectory()) + $source = $source !== '' + ? $component->getDirectory() . Str::of($source)->after($component->getDirectory()) : null; - return Str::afterLast($source,'storage/'); - } - + return Str::afterLast($source, 'storage/'); + } - public function getCleanSourceOnSave(array $data) + /** + * Cleans and formats the source path for saving based on configuration. + * + * @param array $data The form data, including the source URL. + * @return string The cleaned source URL. + */ + public function getCleanSourceOnSave(array $data): string { if (config('filament-tiptap-editor.use_relative_paths')) { $source = Str::of($data['src']) @@ -38,8 +47,14 @@ public function getCleanSourceOnSave(array $data) return $source; } - - + /** + * Mounts the form with the provided arguments, filling the form fields with cleaned source data. + * + * @param TiptapEditor $component The editor component. + * @param ComponentContainer $form The form container. + * @param array $arguments The arguments including media attributes (src, alt, title, etc.). + * @return void + */ protected function getMountWith(TiptapEditor $component, ComponentContainer $form, array $arguments): void { $source = $this->getCleanSource($component, $arguments['src']); @@ -53,7 +68,4 @@ protected function getMountWith(TiptapEditor $component, ComponentContainer $for 'lazy' => $arguments['lazy'] ?? false, ]); } - - - }