bytes-commerce/newsletter-bundle
Installation
composer require bytes-commerce/newsletter-bundle
Add to config/bundles.php:
BytesCommerce\NewsletterBundle\BytesCommerceNewsletterBundle::class => ['all' => true],
Database Migration
Run the bundle’s migration (check src/Resources/migrations/ for schema):
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
First Use Case: Subscribe a User
Inject the NewsletterManager service and call:
$newsletterManager = $this->container->get('bytes_commerce_newsletter.manager');
$newsletterManager->subscribe($user, 'general'); // 'general' is the newsletter type
Configuration
Override defaults in config/packages/bytes_commerce_newsletter.yaml:
bytes_commerce_newsletter:
newsletter_types:
general: {name: 'General Newsletter', description: 'Weekly updates'}
email:
from: 'noreply@example.com'
subject: 'Welcome to our Newsletter!'
Subscription Management
$newsletterManager->subscribe($user, 'general');
$newsletterManager->unsubscribe($user, 'general');
$newsletterManager->toggleSubscription($user, 'general');
Bulk Operations
Use the NewsletterRepository to fetch subscribers:
$subscribers = $newsletterRepository->findByNewsletterType('general');
Event-Driven Integration
Listen for NewsletterSubscribedEvent or NewsletterUnsubscribedEvent:
// src/EventListener/NewsletterListener.php
public function onNewsletterSubscribed(NewsletterSubscribedEvent $event) {
// Send welcome email, log analytics, etc.
}
Custom Newsletter Types
Extend the NewsletterType entity or use the NewsletterTypeManager:
$newsletterTypeManager->createNewsletterType('promotions', [
'name' => 'Promotions',
'description' => 'Exclusive offers',
]);
BytesCommerceNewsletterType form field for subscription forms.{% if app.user is subscribed to 'general' %}
<p>You're subscribed!</p>
{% else %}
<a href="{{ path('subscribe') }}">Subscribe</a>
{% endif %}
#[Route('/subscribe/{type}', name: 'api_subscribe', methods: ['POST'])]
public function subscribe(NewsletterManager $manager, string $type): JsonResponse {
$manager->subscribe($this->getUser(), $type);
return new JsonResponse(['status' => 'subscribed']);
}
Missing Migrations
NewsletterSubscriber table errors.doctrine:migrations:migrate post-install.Newsletter Type Not Found
'promo' instead of 'promotions') throws NewsletterTypeNotFoundException.$types = $newsletterTypeManager->getAllNewsletterTypes();
Duplicate Subscriptions
UNIQUE index:
$table->unique(['user_id', 'newsletter_type']);
Email Configuration Overrides
bytes_commerce_newsletter.email config or extend the NewsletterEmailService.Enable Logging
Add to config/packages/monolog.yaml:
handlers:
bytes_newsletter:
type: stream
path: "%kernel.logs_dir%/newsletter.log"
level: debug
Logs subscription/unsubscription events and errors.
Check Event Dispatching
Ensure events are dispatched by verifying the EventDispatcher is bound to the bundle’s services.
Custom Subscriber Entity
Extend the NewsletterSubscriber entity to add fields (e.g., consent_date):
// src/Entity/CustomNewsletterSubscriber.php
class CustomNewsletterSubscriber extends NewsletterSubscriber {
#[ORM\Column(type: 'datetime')]
private $consentDate;
}
Update the bundle’s Resources/config/doctrine/NewsletterSubscriber.orm.xml to use your entity.
Custom Email Service
Implement BytesCommerce\NewsletterBundle\Service\NewsletterEmailServiceInterface:
class CustomNewsletterEmailService implements NewsletterEmailServiceInterface {
public function sendWelcomeEmail(User $user, string $newsletterType): void {
// Custom logic (e.g., send via Mailgun)
}
}
Register as a service with the bytes_commerce_newsletter.email_service tag.
GDPR Compliance Extensions
Add a consent_required flag to newsletter types and validate in the subscribe method:
if ($newsletterType->isConsentRequired() && !$user->hasConsented()) {
throw new \RuntimeException('Consent required for this newsletter.');
}
'general') are case-sensitive. Use the exact slug defined in newsletter_types.'general' exists by default. Add it to your config if missing:
bytes_commerce_newsletter:
newsletter_types:
general: ~
How can I help you explore Laravel packages today?