Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions app/Helpers/AssetHelpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -699,3 +699,95 @@ function getRewardLootData($showData, $recipient = 'User', $useCustomSelectize =

return $rewardLootData;
}

/**********************************************************************************************

ATTACHMENTS
Similar functionality to the above rewards but agnostic of recipient and other filters.

**********************************************************************************************/

/**
* Gets the valid attachment types.
*
* @return array
*/
function getAttachmentTypes() {
return [
'Item' => 'Item',
'Currency' => 'Currency',
'Character' => 'Character',
'Prompt' => 'Prompt',
'News' => 'News',
'Sales' => 'Sales',
'LootTable' => 'Loot Table',
'Raffle' => 'Raffle Ticket',
'Shop' => 'Shop',
];
}

/**
* Gets the attachment data needed for selection blades.
*
* @return array
*/
function getAttachmentData() {
$attachmentTypes = getAttachmentTypes();

$attachmentLootData = [];

// Iterate through each valid key in $attachmentTypes and get the data associated with it
foreach ($attachmentTypes as $attachmentKey => $attachmentType) {
$query = null;

switch ($attachmentKey) {
case 'Item':
$query = App\Models\Item\Item::orderBy('name');
break;
case 'Currency':
$query = App\Models\Currency\Currency::query()->orderBy('sort_character', 'DESC');
break;
case 'LootTable':
$query = App\Models\Loot\LootTable::orderBy('name');
break;
case 'Raffle':
$query = App\Models\Raffle\Raffle::where('rolled_at', null)->where('is_active', 1)->orderBy('name');
break;
case 'Character':
$query = App\Models\Character\Character::orderBy('slug');
break;
case 'Prompt':
$query = App\Models\Prompt\Prompt::orderBy('name');
break;
case 'News':
$query = App\Models\News::orderBy('title');
break;
case 'Sales':
$query = App\Models\Sales\Sales::orderBy('title');
break;
case 'Shop':
$query = App\Models\Shop\Shop::orderBy('name');
break;
// Add the query builder for your other assets here, set with the matching key in getAttachmentTypes
// If your asset type does not have a model, you may need to add special handling here.
//
// case 'Example':
// $query = \App\Models\Example::orderby('name');
// break;
}

$data = $query->get()->mapWithKeys(function ($item) {
return [
$item->id => json_encode([
'name' => $item->title ?? ($item->fullName ?? $item->name),
'image_url' => $item->imageUrl ?? null,
]),
];
});

// Finally, add the data to the array.
$attachmentLootData[$attachmentKey] = $data;
}

return $attachmentLootData;
}
20 changes: 20 additions & 0 deletions app/Helpers/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -529,3 +529,23 @@ function getRewards($object) {
function hasRewards($object) {
return App\Models\Reward\Reward::where('object_model', get_class($object))->where('object_id', $object->id)->exists();
}

/**
* Returns the given objects attachments, if any.
*
* @param mixed $object
*
* @return bool
*/
function getAttachments($object) {
return App\Models\Attachment::where('parent_model', get_class($object))->where('parent_id', $object->id)->get();
}

/**
* checks if a certain object has any attachments.
*
* @param mixed $object
*/
function hasAttachments($object) {
return App\Models\Attachment::where('parent_model', get_class($object))->where('parent_id', $object->id)->exists();
}
31 changes: 31 additions & 0 deletions app/Http/Controllers/Admin/AttachmentController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Services\AttachmentService;
use Illuminate\Http\Request;

class AttachmentController extends Controller {
/**
* Creates or edits an objects attachments.
*
* @param App\Services\AttachmentService $service
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postCreateEditAttachments(Request $request, AttachmentService $service) {
$data = $request->only([
'parent_model', 'parent_id', 'attachment_type', 'attachment_id', 'data',
]);
if ($service->editAttachments($data['parent_model'], $data['parent_id'], $data)) {
flash('Attachments updated successfully.')->success();
} else {
foreach ($service->errors()->getMessages()['error'] as $error) {
flash($error)->error();
}
}

return redirect()->back();
}
}
101 changes: 101 additions & 0 deletions app/Models/Attachment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

namespace App\Models;

class Attachment extends Model {
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'parent_model', 'parent_id', 'attachment_type', 'attachment_id', 'data',
];

/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'attachments';

/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'data' => 'array',
];

/**********************************************************************************************

RELATIONS

**********************************************************************************************/

/**
* Get the parent object this is attached to.
*/
public function parent() {
return $this->belongsTo($this->parent_model, 'parent_id');
}

/**
* Get the attached object.
*/
public function attachment() {
$model = getAssetModelString(strtolower($this->attachment_type));

if (!class_exists($model)) {
// Laravel requires a relationship instance to be returned (cannot return null), so returning one that doesn't exist here.
return $this->belongsTo(self::class, 'id', 'attachment_id')->whereNull('attachment_id');
}

return $this->belongsTo($model, 'attachment_id');
}

/**********************************************************************************************

ATTRIBUTES

**********************************************************************************************/

/**
* Returns the description attribute or null if not set.
*/
public function getDescriptionAttribute() {
return $this->data['description'] ?? null;
}

/**
* Returns the parsed description attribute or null if not set.
*/
public function getParsedDescriptionAttribute() {
return $this->data['parsed_description'] ?? null;
}

/**********************************************************************************************

OTHER FUNCTIONS

**********************************************************************************************/

/**
* Checks if a certain object has any attachments.
*
* @param mixed $object
*/
public static function hasAttachments($object) {
return self::where('parent_model', get_class($object))->where('parent_id', $object->id)->exists();
}

/**
* Get the attachments of a certain object.
*
* @param mixed $object
*/
public static function getAttachments($object) {
return self::where('parent_model', get_class($object))->where('parent_id', $object->id)->get();
}
}
108 changes: 108 additions & 0 deletions app/Services/AttachmentService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

namespace App\Services;

use App\Models\Attachment;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

class AttachmentService extends Service {
/*
|--------------------------------------------------------------------------
| Attachment Service
|--------------------------------------------------------------------------
|
| Handles the creation and editing of attachments on objects
|
*/

/**********************************************************************************************

ATTACHMENTS

**********************************************************************************************/

/**
* Creates or edits attachments on an object.
* Deletes existing attachments for the given parent, then recreates from provided data.
*
* @param string $parent_model
* @param int $parent_id
* @param array $data
* @param mixed $log
*
* @return bool
*/
public function editAttachments($parent_model, $parent_id, $data, $log = true) {
DB::beginTransaction();

try {
$parent = $parent_model::find($parent_id);
if (!$parent) {
throw new \Exception('Object not found.');
}

$attachments = hasAttachments($parent) ? getAttachments($parent) : [];
if (count($attachments) > 0) {
$attachments->each(function ($attachment) {
$attachment->delete();
});
}

if (count($attachments) > 0) {
flash('Deleted '.count($attachments).' old attachments.')->success();
}

if (isset($data['attachment_type'])) {
foreach ($data['attachment_type'] as $key => $type) {
$attributes = [
'parent_model' => $parent_model,
'parent_id' => $parent_id,
'attachment_type' => $data['attachment_type'][$key],
'attachment_id' => $data['attachment_id'][$key],
'data' => null,
];

if (isset($data['data'][$key])) {
// description is special we also store parsed_description
if (isset($data['data'][$key]['description'])) {
$attributes['data']['description'] = $data['data'][$key]['description'];
$attributes['data']['parsed_description'] = parse($data['data'][$key]['description']);

unset($data['data'][$key]['description']);
}
// additional "special" fields can be mapped here in the future.

// any other fields are stored as-is
foreach ($data['data'][$key] as $field_key => $field_value) {
$attributes['data'][$field_key] = $field_value;
}
}

$attachment = new Attachment([
'parent_model' => $parent_model,
'parent_id' => $parent_id,
'attachment_type' => $data['attachment_type'][$key],
'attachment_id' => $data['attachment_id'][$key],
'data' => $attributes['data'] ?? null,
]);

if (!$attachment->save()) {
throw new \Exception('Failed to save attachment.');
}
}
}

// Log the action
if ($log && !$this->logAdminAction(Auth::user(), 'Edited Attachments', 'Edited '.$parent->displayName.' attachments')) {
throw new \Exception('Failed to log admin action.');
}

return $this->commitReturn(true);
} catch (\Exception $e) {
$this->setError('error', $e->getMessage());
}

return $this->rollbackReturn(false);
}
}
29 changes: 29 additions & 0 deletions database/migrations/2026_01_11_172249_create_attachments_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

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('attachments', function (Blueprint $table) {
$table->id();
$table->string('parent_model');
$table->unsignedBigInteger('parent_id');
$table->string('attachment_type');
$table->unsignedBigInteger('attachment_id');

$table->json('data')->nullable()->default(null); // for extensible data storage / extra functionality
});
}

/**
* Reverse the migrations.
*/
public function down(): void {
Schema::dropIfExists('attachments');
}
};
Loading