Created
August 1, 2020 03:22
-
-
Save blood72/a0f2a2fdf8ade5a4798444eda3870ff7 to your computer and use it in GitHub Desktop.
Attachment model (personal use)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Illuminate\Database\Migrations\Migration; | |
use Illuminate\Database\Schema\Blueprint; | |
use Illuminate\Support\Facades\Schema; | |
class CreateAttachmentsTable extends Migration | |
{ | |
/** | |
* Run the migrations. | |
* | |
* @return void | |
*/ | |
public function up() | |
{ | |
Schema::create('attachments', function (Blueprint $table) { | |
$table->id(); | |
$table->nullableMorphs('attachable'); | |
$table->string('disk', 20); | |
$table->string('collection')->default('default'); | |
$table->integer('size')->unsigned()->nullable(); | |
$table->string('mime')->nullable(); | |
$table->string('path')->nullable(); | |
$table->boolean('visibility')->default(true); | |
$table->boolean('protection')->default(false); | |
$table->timestamps(); | |
}); | |
} | |
/** | |
* Reverse the migrations. | |
* | |
* @return void | |
*/ | |
public function down() | |
{ | |
Schema::dropIfExists('attachments'); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Models\File; | |
use App\Models\Model; | |
use Illuminate\Contracts\Filesystem\Filesystem; | |
use Illuminate\Support\Facades\Storage; | |
class Attachment extends Model | |
{ | |
/** @var \Illuminate\Contracts\Filesystem\Filesystem|\Illuminate\Filesystem\FilesystemAdapter */ | |
protected Filesystem $adapter; | |
/** | |
* @return \Illuminate\Database\Eloquent\Relations\MorphTo | |
*/ | |
public function attachable() | |
{ | |
return $this->morphTo(); | |
} | |
/** | |
* Check that it is in safe-mode. | |
* | |
* @return bool | |
*/ | |
public function isProtected(): bool | |
{ | |
return $this->protection; | |
} | |
/** | |
* Check a directory has no files. | |
* | |
* @param string $path | |
* @return bool | |
*/ | |
protected function isEmptyDirectory(string $path): bool | |
{ | |
return empty($this->adapter->allFiles($path)); | |
} | |
/** | |
* Get a file directory path. | |
* | |
* @param string|null $path | |
* @return string | |
*/ | |
public function getDirectoryPath(?string $path = null): string | |
{ | |
return pathinfo($path ?: $this->path, PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR; | |
} | |
/** | |
* Set a filesystem instance driver disk. | |
* | |
* @param string|null $disk | |
* @return void | |
*/ | |
protected function setAdapterDisk(?string $disk): void | |
{ | |
$this->adapter = Storage::disk($disk); | |
} | |
/** | |
* Delete associated all files. | |
* | |
* @return void | |
*/ | |
protected function deleteAssociateFiles() | |
{ | |
if ($this->isProtected()) { | |
return; | |
} | |
$this->adapter->delete($this->path); | |
if ($directory = $this->getDirectoryPath() and $this->isEmptyDirectory($directory)) { | |
$this->adapter->deleteDirectory($directory); | |
} | |
} | |
/** | |
* Attachment constructor. | |
* | |
* @param array $attributes | |
* @return void | |
*/ | |
public function __construct(array $attributes = []) | |
{ | |
parent::__construct($attributes); | |
$this->setAdapterDisk($this->disk); | |
} | |
/** | |
* The "booting" method of the model. | |
* | |
* @return void | |
*/ | |
public static function boot() | |
{ | |
parent::boot(); | |
static::deleting(function (self $attachment) { | |
$attachment->deleteAssociateFiles(); | |
}); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Traits; | |
use App\Models\File\Attachment; | |
trait HasAttachments | |
{ | |
/** | |
* @see Attachment | |
* @return \Illuminate\Database\Eloquent\Relations\MorphMany | |
*/ | |
public function attachments() | |
{ | |
return $this->morphMany(Attachment::class, 'attachable'); | |
} | |
/** | |
* The "booting" method of the model. | |
* | |
* @return void | |
*/ | |
protected static function bootHasAttachments() | |
{ | |
static::deleting(function ($model) { | |
if (method_exists($model, 'isForceDeleting') && ! $model->isForceDeleting()) { | |
return; | |
} | |
foreach ($model->attachments as $attachment) { | |
/** @var Attachment|\Illuminate\Database\Eloquent\Model $attachment */ | |
if ($attachment->isProtected()) { | |
return; | |
} | |
$attachment->delete(); | |
} | |
}); | |
} | |
/** | |
* Define a polymorphic one-to-many relationship. | |
* | |
* @param string $related | |
* @param string $name | |
* @param string|null $type | |
* @param string|null $id | |
* @param string|null $localKey | |
* @return \Illuminate\Database\Eloquent\Relations\MorphMany | |
*/ | |
abstract public function morphMany($related, $name, $type = null, $id = null, $localKey = null); | |
/** | |
* Register a deleting model event with the dispatcher. | |
* | |
* @param \Closure|string $callback | |
* @return void | |
*/ | |
abstract public static function deleting($callback); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Console\Commands; | |
use App\Models\File\Attachment; | |
use Illuminate\Console\Command; | |
use Illuminate\Support\Facades\Storage; | |
class RegisterAttachments extends Command | |
{ | |
/** | |
* The name and signature of the console command. | |
* | |
* @var string | |
*/ | |
protected $signature = 'attachment:register {--disk=} {--prefix=} {--collection=default} {--protection}'; | |
/** | |
* The console command description. | |
* | |
* @var string | |
*/ | |
protected $description = 'Register attachments'; | |
/** @var \Illuminate\Contracts\Filesystem\Filesystem|\Illuminate\Filesystem\FilesystemAdapter */ | |
protected $adapter; | |
/** | |
* Execute the console command. | |
* | |
* @return int | |
*/ | |
public function handle() | |
{ | |
$this->setAdapterDisk(); | |
$this->info('Calculating attachments..'); | |
$paths = $this->getRegisterNeededFiles(); | |
if (empty($amount = count($paths))) { | |
$this->info('No need to register.'); | |
return -1; | |
} | |
$rows = []; | |
$bar = $this->output->createProgressBar($amount); | |
foreach ($paths as $path) { | |
$rows[] = $this->setAttachmentRow($path); | |
$bar->advance(); | |
} | |
$bar->finish(); | |
$this->info("\nRegistering attachments.."); | |
Attachment::insert($rows); | |
$this->info("\nDone."); | |
return 0; | |
} | |
/** | |
* @param string $path | |
* @return array | |
*/ | |
protected function setAttachmentRow($path) | |
{ | |
static $now; | |
$now ??= now(); | |
return [ | |
'disk' => $this->getDiskName(), | |
'collection' => $this->option('collection'), | |
'size' => $this->adapter->size($path), | |
'mime' => $this->adapter->mimeType($path), | |
'path' => $path, | |
'visibility' => $this->adapter->getVisibility($path) !== 'private' ? true : false, | |
'protection' => (bool) $this->option('protection'), | |
'created_at' => $now, | |
'updated_at' => $now, | |
]; | |
} | |
/** | |
* @return array | |
*/ | |
protected function getRegisterNeededFiles() | |
{ | |
return array_diff( | |
$this->adapter->allFiles($this->option('prefix')), | |
$this->getRegisteredAttachments(), | |
); | |
} | |
/** | |
* @return array | |
*/ | |
protected function getRegisteredAttachments() | |
{ | |
return Attachment::where('disk', $this->getDiskName())->pluck('path')->toArray(); | |
} | |
/** | |
* @return string | |
*/ | |
protected function getDiskName() | |
{ | |
return $this->option('disk') ?: config('filesystems.default'); | |
} | |
/** | |
* Set a filesystem instance driver disk. | |
* | |
* @return void | |
*/ | |
protected function setAdapterDisk(): void | |
{ | |
$this->adapter = Storage::disk($this->getDiskName()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment