Updated 15 June 2026
In Bagisto, every product type quietly hides a few fields — a virtual product has no weight, and a booking product has no guest checkout option.
Our seller Quick Create form had to hide those same fields per type, so the screen only ever shows the inputs that actually make sense.
The easy way is to hardcode that list and move on. The better way is to Read Extended Protected Properties straight from each product type.
The quick fix is to simply list what each type skips:
|
1 2 3 4 |
$skip = match ($type) { 'virtual', 'configurable', 'booking' => ['weight', 'width', 'height', 'depth'], default => [], }; |
This works today. But the day Bagisto changes a type, or adds a brand new one, the list is suddenly wrong, and a field shows up where it should not.
Worse, nothing ever warns you about it. The list just sits in our code and slowly drifts out of date with every framework update.
Each product type already stores this list in a protected $skipAttributes property — readable only inside the class and its children, never from our form.
PHP’s Reflection lets us open the class at runtime, unlock the property with setAccessible, and read its value as if it were public:

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
trait InteractsWithQuickCreateTypes { /** * Get the attribute codes a given product type skips (e.g. simple has none, * virtual/configurable/booking skip weight & dimensions). Read from the protected * $skipAttributes on the type class so it stays in sync with core, never hardcoded. */ protected function typeSkipAttributes(string $type): array { $class = config("product_types.$type.class"); if (! $class || ! class_exists($class)) { return []; } try { $instance = app($class); $property = new ReflectionProperty($instance, 'skipAttributes'); $property->setAccessible(true); return (array) $property->getValue($instance); } catch (Throwable $e) { return []; } } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class QuickCreateController extends Controller { use InteractsWithQuickCreateTypes; public function index(Request $request): View { // One call gives us the exact fields to hide for this type. $type = $request->input('type', 'simple'); $skipAttributes = $this->typeSkipAttributes($type); return view('marketplace::seller.settings.quick-create.index') ->with([ 'type' => $type, 'skipAttributes' => $skipAttributes, ]); } } |
Now the list lives in exactly one place — the product type itself — and our form simply mirrors it, picking up any change on the very next request.
Reading a protected property does tie us to an internal detail rather than a public method — a small risk if core ever renames it later.
That is exactly why the try/catch returns an empty list on any failure, so the worst case becomes “show every field,” never a fatal error.
It runs once per request, so the cost is tiny, and it needs no upkeep — but if a clean public method ever appears, prefer that instead.
Reaching in to Read Extended Protected Properties is fine when the value already lives somewhere and a child class owns it.
Guard it with try/catch, keep it to one place, and switch to a public method the day core gives you one.
If you have more details or questions, you can reply to the received confirmation email.
Back to Home
Be the first to comment.