Updated 2 July 2026
The Bagisto Proxy Pattern is one of the framework’s most powerful extension mechanisms.
It lets developers override core models without editing vendor files or forking the platform.
You might add a method to the Product model, modify a relationship, or replace an entire model with a custom implementation.
In most frameworks that means editing vendor files (wiped on the next update) or forking the platform (which you then maintain forever).
Bagisto Dev Doc solves this with the Proxy Pattern.
It adds a thin layer of indirection that lets any package replace a core model at runtime.
No changes to the core code are required.

Product means editing core – lost on the next composer update.ProductProxy::modelClass(), resolved to whatever is registered.Bagisto sits on Konekt Concord, a Laravel modularization library.
Every model is paired with two small companions: a Contract and a Proxy.
The Contract defines the model’s interface, while the Proxy resolves that contract to the concrete model currently registered.
Application code references the proxy, never the concrete class.

The contract is just an empty interface that gives the model a stable, abstract name:
|
1 2 3 4 |
<?php // packages/Webkul/Product/src/Contracts/Product.php namespace Webkul\Product\Contracts; interface Product {} |
The proxy has no body at all – all the intelligence lives in the parent ModelProxy:
|
1 2 3 4 5 |
<?php // packages/Webkul/Product/src/Models/ProductProxy.php namespace Webkul\Product\Models; use Konekt\Concord\Proxies\ModelProxy; class ProductProxy extends ModelProxy {} |
Each package’s ModuleServiceProvider lists the models it owns in a single array:
|
1 2 3 4 5 6 7 8 9 10 11 |
<?php // packages/Webkul/Product/src/Providers/ModuleServiceProvider.php class ModuleServiceProvider extends CoreModuleServiceProvider { protected $models = [ Product::class, ProductImage::class, ProductInventory::class, // ...more ]; } |
On boot, Concord derives each model’s contract and binds the pair in the container:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php // vendor/konekt/concord/src/BaseServiceProvider.php protected function registerModels() { foreach ($this->models as $key => $model) { $contract = is_string($key) ? $key : $this->convention->contractForModel($model); $this->concord->registerModel( $contract, $model, config('concord.register_route_models', true) ); } } |
Relationships never name a concrete class – they call ::modelClass() on the proxy:
|
1 2 3 4 5 6 7 8 9 |
<?php // packages/Webkul/Product/src/Models/Product.php public function categories(): BelongsToMany { return $this->belongsToMany( CategoryProxy::modelClass(), 'product_categories' ); } |
The convention runs both directions – a wishlist points back at a product the same way:
modelClass() Resolves the Real Class|
1 2 3 4 5 6 7 8 9 10 11 |
<?php // vendor/konekt/concord/src/Proxies/ModelProxy.php public static function modelClass() { return static::getInstance()->targetClass(); } protected function targetClass(): string { return $this->concord->model($this->contract); } |
|
1 2 3 4 5 6 |
<?php // vendor/konekt/concord/src/Concord.php public function model(string $abstract) { return Arr::get($this->models, $abstract); } |
You can also call model methods directly on the proxy.
The __callStatic method forwards each call to the real model class.
|
1 2 3 4 5 6 7 8 9 |
<?php // vendor/konekt/concord/src/Proxies/BaseProxy.php public static function __callStatic($method, $parameters) { return call_user_func( static::getInstance()->targetClass() . '::' . $method, ...$parameters ); } |

|
1 2 3 4 5 6 7 8 9 10 11 |
<?php // packages/YourVendor/Catalog/src/Models/Product.php namespace YourVendor\Catalog\Models; use Webkul\Product\Models\Product as BaseProduct; class Product extends BaseProduct { public function isEligibleForFreeShipping(): bool { return$this->price >= 100; } } |
|
1 2 3 4 5 |
<?php // packages/Webkul/Marketplace/src/Providers/MarketplaceManager.php $this->app->concord->registerModel(ProductContract::class, Product::class); $this->app->concord->registerModel(CartItemContract::class, CartItem::class); |
|
1 2 3 4 5 6 7 8 9 10 11 |
<?php // vendor/konekt/concord/src/Concord.php public function registerModel(string $abstract, string $concrete, $registerRouteModel = true) { if (!is_subclass_of($concrete, $abstract, true)) { thrownewInvalidArgumentException("Class {$concrete} must extend or implement {$abstract}."); } $this->models[$abstract] = $concrete; $this->app->alias($concrete, $abstract); // ...re-registers route models and resets the cached proxy } |
By pairing every model with a contract and a proxy, Bagisto makes model substitution simple.
What is usually a fork-or-suffer problem becomes a one-line registration.
Your customizations stay outside the core, making upgrades much easier.
You can also hire Laravel developers to build custom solutions on Laravel. To explore available extensions, check out the Bagisto Extension marketplace.
If you have more details or questions, you can reply to the received confirmation email.
Back to Home
Be the first to comment.