Installation:
composer require sylius/channel
Add the bundle to config/bundles.php (if using Symfony) or register the component in your Laravel service provider.
Configuration:
Define channels in config/channel.php (or equivalent):
'channels' => [
'web' => [
'name' => 'Main Website',
'code' => 'WEB',
'enabled' => true,
'prices' => ['USD', 'EUR'],
'currencies' => ['USD', 'EUR'],
'locales' => ['en_US', 'fr_FR'],
'tax_categories' => ['standard', 'reduced'],
],
],
First Use Case: Fetch a channel by code in a controller:
use Sylius\Component\Channel\Repository\ChannelRepositoryInterface;
public function show(ChannelRepositoryInterface $channelRepository)
{
$channel = $channelRepository->findOneBy(['code' => 'WEB']);
return view('channel.show', ['channel' => $channel]);
}
Channel (entity representing a sales channel)ChannelRepository (for fetching channels)ChannelContext (for context-aware operations)ChannelAwareInterface (for entities that need channel context)Extend or implement ChannelAwareInterface for entities like products, taxes, or promotions:
use Sylius\Component\Channel\ChannelAwareInterface;
class Product implements ChannelAwareInterface
{
private ?ChannelInterface $channel = null;
public function getChannel(): ?ChannelInterface
{
return $this->channel;
}
public function setChannel(?ChannelInterface $channel): void
{
$this->channel = $channel;
}
}
Use ChannelContext to resolve the current channel (e.g., from request or session):
use Sylius\Component\Channel\Context\ChannelContextInterface;
public function getCurrentChannel(ChannelContextInterface $channelContext)
{
return $channelContext->getChannel();
}
Store channel-specific configurations in a ChannelPricing or ChannelTaxation entity:
use Sylius\Component\Channel\ChannelInterface;
class ProductPricing
{
private ChannelInterface $channel;
private float $price;
// Getters/setters...
}
Filter resources by channel in API routes or queries:
// Example: API route for channel-specific products
Route::get('/channels/{code}/products', [ProductController::class, 'index'])
->where('code', '[A-Za-z0-9_-]+');
Listen for ChannelCreatedEvent or ChannelUpdatedEvent to trigger side effects:
use Sylius\Component\Channel\Event\ChannelCreatedEvent;
public function onChannelCreated(ChannelCreatedEvent $event)
{
// Sync channel with external services, e.g., CDN or analytics
}
Service Provider Binding:
Bind the repository and context in AppServiceProvider:
public function register()
{
$this->app->bind(ChannelRepositoryInterface::class, function ($app) {
return new ChannelRepository($app->make(EntityManagerInterface::class]);
});
}
Request-Based Channel Resolution: Resolve the channel from the request in middleware:
public function handle(Request $request, Closure $next)
{
$channelCode = $request->get('channel', config('channel.default'));
$channel = app(ChannelRepositoryInterface::class)->findOneBy(['code' => $channelCode]);
app(ChannelContextInterface::class)->setChannel($channel);
return $next($request);
}
Eloquent Model Extensions: Add channel-aware behavior to Eloquent models:
use Sylius\Component\Channel\ChannelAwareInterface;
class Product extends Model implements ChannelAwareInterface
{
use ChannelAwareTrait; // Hypothetical trait for Sylius-style channel handling
}
Caching Channel Data: Cache channel configurations to avoid repeated DB lookups:
$channel = Cache::remember("channel:{$code}", now()->addHours(1), function () use ($code) {
return $channelRepository->findOneBy(['code' => $code]);
});
Channel Context Leaks:
ChannelContext before accessing channel-aware entities.NullChannelException if a channel-aware entity lacks context.Overlapping Channel Configurations:
$channel->validate(); // If using Sylius' validation
Performance with N+1 Queries:
with() in queries or DTOs to preload channel data:
$products = Product::with(['channel', 'prices.channel'])->get();
Locale/Currency Mismatches:
Channel entity or use a ChannelValidator:
$channel->getLocales()->mustBeSubsetOf(app()->getLocales());
Hardcoded Channel Codes:
'WEB').final class ChannelCodes
{
public const WEB = 'WEB';
public const MOBILE = 'MOBILE';
}
Log Channel Context: Add a debug middleware to log the current channel:
public function handle($request, Closure $next)
{
\Log::debug('Current channel:', [
'code' => app(ChannelContextInterface::class)->getChannel()?->getCode(),
]);
return $next($request);
}
Validate Channel Entities: Use Sylius' validation or custom rules to catch issues early:
$validator = Validator::make($channelData, [
'code' => 'required|unique:channels|size:3',
'locales' => 'array|min:1',
]);
Test Channel Isolation: Write tests to ensure channel-specific logic works in isolation:
public function test_channel_specific_pricing()
{
$channel = $this->createChannel(['code' => 'WEB']);
$product = $this->createProduct(['channel' => $channel]);
$this->assertEquals(19.99, $product->getPrice()->getAmount());
}
Custom Channel Types:
Extend the Channel entity to add custom fields:
class CustomChannel extends Channel
{
private ?string $theme = null;
// Add getters/setters and DB mapping
}
Dynamic Channel Routing: Use a router to dynamically generate channel-specific routes:
Route::prefix('{channel_code}')->group(function () {
Route::get('/products', [ProductController::class, 'index']);
});
Channel-Specific Middleware: Apply middleware based on the current channel:
$channel = app(ChannelContextInterface::class)->getChannel();
if ($channel->getCode() === 'MOBILE') {
$request->headers->set('X-Channel', 'MOBILE');
}
Event Subscribers: Subscribe to channel events for cross-cutting concerns:
use Sylius\Component\Channel\Event\ChannelCreatedEvent;
public static function getSubscribedEvents()
{
return [
ChannelCreatedEvent::class => 'onChannelCreated',
];
}
API Resource Transformers: Use Fractal or similar to transform channel-aware entities:
$transformer = new ProductTransformer();
$resource = $transformer->transform($product, (new Data::class)->withChannel());
How can I help you explore Laravel packages today?