Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here

database/migrations/2020_06_30_000000_create_attachments_table.php

Open in GitHub
public function up(): void
{
Schema::create('attachments', function (Blueprint $table) {
$table->increments('id');
$table->morphs('attachable');
$table->string('file');
$table->timestamps();
$table->softDeletes();
});
}

app/Models/Attachment.php

Open in GitHub
use Azuriom\Models\Traits\HasImage;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
 
class Attachment extends Model
{
use HasImage;
use SoftDeletes;
 
protected string $imageKey = 'file';
 
public function attachable()
{
return $this->morphTo('attachable');
}
 
protected function resolveImagePath(string $path = ''): string
{
return $this->attachable->getAttachmentsPath().'/'.$path;
}
}

app/Models/Traits/Attachable.php

Open in GitHub
use Azuriom\Models\Attachment;
use Azuriom\Models\PendingAttachment;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;
 
trait Attachable
{
public static function bootAttachable(): void
{
static::updated(function (self $model) {
$content = $model->getAttribute($model->getAttachmentsKey());
 
if ($model->getOriginal($model->getAttachmentsKey()) === $content) {
return;
}
 
$attachments = $model->attachments()->withTrashed()->get();
 
foreach ($attachments as $attachment) {
if (Str::contains($content, $attachment->file)) {
if ($attachment->trashed()) {
$attachment->restore();
}
} elseif (! $attachment->trashed()) {
$attachment->delete();
}
}
});
 
static::deleted(function (self $model) {
$attachments = $model->attachments()->withTrashed()->get();
 
foreach ($attachments as $attachment) {
$attachment->setRelation('attachable', $model)->forceDelete();
}
});
}
 
public static function storePendingAttachment(string $pendingId, UploadedFile $file): string
{
$attachment = new PendingAttachment([
'pending_id' => $pendingId,
'pending_type' => (new self())->getAttachmentsType(),
]);
 
return $attachment->storeImage($file, true);
}
 
public function persistPendingAttachments(?string $pendingId): void
{
if ($pendingId === null) {
return;
}
 
$attachments = PendingAttachment::where('pending_id', $pendingId)
->where('pending_type', $this->getAttachmentsType())
->get();
 
$content = $this->getAttribute($this->getAttachmentsKey());
 
foreach ($attachments as $attachment) {
if (Str::contains($content, $attachment->file)) {
$this->attachments()
->make()
->forceFill(['file' => $attachment->file])
->save();
 
// We don't want to delete the file since now a permanent attachment use it
$attachment->forceFill(['file' => null]);
}
 
// Delete the pending attachment, and the file if it was not used
$attachment->delete();
}
}
 
public function storeAttachment(UploadedFile $file): string
{
/** @var \Azuriom\Models\Attachment $attachment */
$attachment = $this->attachments()->make();
 
return $attachment->storeImage($file, true);
}
 
public function attachments()
{
return $this->morphMany(Attachment::class, 'attachable');
}
 
public function getAttachmentsKey(): string
{
return 'content';
}
 
public function getAttachmentsType(): string
{
return (new self())->getMorphClass();
}
 
public function getAttachmentsPath(): string
{
return Str::replace('_', '/', $this->getTable()).'/attachments';
}
}

app/Http/Controllers/Admin/PageAttachmentController.php

Open in GitHub
use Azuriom\Http\Controllers\Controller;
use Azuriom\Http\Requests\AttachmentRequest;
use Azuriom\Models\Page;
 
class PageAttachmentController extends Controller
{
public function store(AttachmentRequest $request, Page $page)
{
$imageUrl = $page->storeAttachment($request->file('file'));
 
return response()->json(['location' => $imageUrl]);
}
 
// ...
}

app/Models/Page.php

Open in GitHub
class Page extends Model
{
use Attachable;
use Loggable;
 
// ...

app/Models/Post.php

Open in GitHub
class Post extends Model
{
use Attachable;
use HasFactory;
use HasImage;
use HasUser;
use Loggable;
 
// ...

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.