Installation Add the package via Composer:
composer require appventus/multi-domain-bundle:dev-master
Register the bundle in config/bundles.php:
return [
// ...
AppVentus\MultiDomainBundle\AvMultiDomainBundle::class => ['all' => true],
];
Entity Integration
Add the DomainTrait to any entity you want to restrict by domain (e.g., Page):
use AppVentus\MultiDomainBundle\Traits\DomainTrait;
/**
* @ORM\Entity
*/
class Page
{
use DomainTrait;
// ...
}
First Use Case
domain field (auto-added by the trait).app.example.com/pages/1).Route Annotations: Use Symfony’s routing with domain constraints:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Domain;
/**
* @Route("/pages/{id}", name="page_show")
* @Domain("example.com")
*/
public function showPage(Page $page) { ... }
Note: The bundle doesn’t enforce this natively; combine with stof/doctrine-extensions-bundle for @Domain support.
Dynamic Domain Resolution:
Override the default domain resolver (e.g., in a custom DomainResolver service) to fetch domains from a database or API:
// config/services.yaml
AppVentus\MultiDomainBundle\DomainResolverInterface: '@app.custom_domain_resolver'
Repository Methods: Extend entity repositories to leverage domain filtering:
class PageRepository extends ServiceEntityRepository
{
public function findByDomain(string $domain): array
{
return $this->createQueryBuilder('p')
->andWhere('p.domain = :domain')
->setParameter('domain', $domain)
->getQuery()
->getResult();
}
}
Event Listeners: Attach listeners to filter collections globally (e.g., in PageRepository):
use AppVentus\MultiDomainBundle\EventListener\DomainFilterListener;
// config/services.yaml
AppVentus\MultiDomainBundle\EventListener\DomainFilterListener:
tags:
- { name: doctrine.event_listener, event: onFind }
Database Connections: Combine with doctrine/dbal to switch databases per domain:
// Custom DomainResolver
public function getDomain(): string
{
$domain = parent::getDomain();
$connection = $this->connectionManager->getConnectionForDomain($domain);
// ...
}
Shared vs. Isolated Data:
DomainTrait for shared data (e.g., global settings).tenant_id field and filter manually.Missing Domain Field:
The trait adds a domain field automatically, but ensure your database schema includes it:
php bin/console doctrine:schema:update --force
Case Sensitivity: Domains are case-sensitive by default. Normalize them in the resolver:
return strtolower($request->getHost());
Caching Issues: Doctrine’s second-level cache may serve stale domain-filtered results. Exclude cached queries:
->useResultCache(false)
->useQueryCache(false)
Subdomains vs. Domains:
The bundle treats app.example.com and example.com as distinct. Use regex in the resolver if needed:
if (preg_match('/^(app\.)?example\.com$/', $host)) { ... }
Check Current Domain:
Inject DomainResolverInterface into controllers to debug:
public function index(DomainResolverInterface $resolver)
{
dump($resolver->getDomain()); // e.g., "example.com"
}
Query Logs: Enable Doctrine logging to verify filters:
# config/packages/doctrine.yaml
doctrine:
dbal:
logging: true
profiling: true
Custom Domain Sources:
Override DomainResolverInterface to fetch domains from a custom source (e.g., Redis):
class RedisDomainResolver implements DomainResolverInterface
{
public function getDomain(): string
{
return $this->redis->get('current_domain');
}
}
Dynamic Entity Filtering:
Extend the DomainTrait to support multi-domain entities:
trait MultiDomainTrait extends DomainTrait
{
public function isDomainMatch(string $domain): bool
{
return in_array($domain, $this->domains);
}
}
Fallback Domains:
Configure a fallback domain in config/packages/av_multi_domain.yaml:
av_multi_domain:
fallback_domain: "default.example.com"
Index the Domain Field: Add a database index to speed up queries:
/**
* @ORM\Table(indexes={@ORM\Index(name="idx_domain", columns={"domain"})})
*/
class Page { ... }
Batch Domain Updates: Use Doctrine’s bulk operations for mass domain assignments:
$em->createQueryBuilder()
->update(Page::class, 'p')
->set('p.domain', ':domain')
->where('p.domain IS NULL')
->setParameter('domain', 'example.com')
->getQuery()
->execute();
How can I help you explore Laravel packages today?