2026-09-23 17:53:23 +03:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
|
|
|
|
use Illuminate\Http\Request;
|
2026-09-25 13:47:58 +03:00
|
|
|
use Modules\Core\File\Http\Controllers\UploadFileController;
|
2026-09-23 17:53:23 +03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stores the shopper's photo for a product custom field of type `file` (see
|
|
|
|
|
* boboko-core's Product::$custom_fields) the moment it's picked on the product
|
|
|
|
|
* page — before add-to-cart, see custom-field-upload-controller.js.
|
|
|
|
|
*
|
2026-09-25 13:47:58 +03:00
|
|
|
* Which files are acceptable (extensions, size) is a per-site decision — this
|
|
|
|
|
* site: photographs — so it lives here as this app's own policy, extending
|
|
|
|
|
* boboko-core's Modules\Core\File\Http\Controllers\UploadFileController for
|
|
|
|
|
* the actual store()/validate()/respond() mechanics. The module's add-to-cart
|
|
|
|
|
* endpoint only ever receives the stored File row's own `id` — FileService is
|
|
|
|
|
* the single source of truth for disk/path/name/mime, never duplicated into
|
|
|
|
|
* cart/order line meta (see Checkout\CartController::customFieldsMeta()). A
|
|
|
|
|
* shopper can't point a cart line at someone else's file: CartController only
|
|
|
|
|
* accepts an id that is both unowned and tagged with this exact PURPOSE.
|
2026-09-23 17:53:23 +03:00
|
|
|
*
|
|
|
|
|
* Stored on the private `local` disk: these are customers' personal photos,
|
2026-09-25 13:47:58 +03:00
|
|
|
* never reachable by a public URL except through FileService's own signed
|
|
|
|
|
* download route. Uploads nobody adds to a cart are removed by core's
|
|
|
|
|
* `boboko:file:prune-unowned custom-field-upload` command.
|
2026-09-23 17:53:23 +03:00
|
|
|
*/
|
2026-09-25 13:47:58 +03:00
|
|
|
class CustomFieldUploadController extends UploadFileController
|
2026-09-23 17:53:23 +03:00
|
|
|
{
|
2026-09-25 13:47:58 +03:00
|
|
|
public const PURPOSE = 'custom-field-upload';
|
2026-09-23 17:53:23 +03:00
|
|
|
|
|
|
|
|
public const MAX_KILOBYTES = 10240;
|
|
|
|
|
|
|
|
|
|
public const EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'heic', 'heif'];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* For the file input's `accept` attribute — same list the server enforces.
|
|
|
|
|
*/
|
|
|
|
|
public static function accept(): string
|
|
|
|
|
{
|
|
|
|
|
return '.'.implode(',.', self::EXTENSIONS);
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-25 13:47:58 +03:00
|
|
|
protected function purpose(): string
|
2026-09-23 17:53:23 +03:00
|
|
|
{
|
2026-09-25 13:47:58 +03:00
|
|
|
return self::PURPOSE;
|
|
|
|
|
}
|
2026-09-23 17:53:23 +03:00
|
|
|
|
2026-09-25 13:47:58 +03:00
|
|
|
protected function validationRules(Request $request): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
'file' => ['required', 'file', 'mimes:'.implode(',', self::EXTENSIONS), 'max:'.self::MAX_KILOBYTES],
|
|
|
|
|
];
|
|
|
|
|
}
|
2026-09-23 17:53:23 +03:00
|
|
|
|
2026-09-25 13:47:58 +03:00
|
|
|
// `label` is the admin-authored field label, only used as the
|
|
|
|
|
// :attribute in the validation message shown next to that field.
|
|
|
|
|
protected function validationAttributes(Request $request): array
|
|
|
|
|
{
|
|
|
|
|
return ['file' => (string) $request->input('label', 'file')];
|
2026-09-23 17:53:23 +03:00
|
|
|
}
|
|
|
|
|
}
|