Newsletter bundle
Introduction
Overview
With enhavo you are able to set global values for your storage and strategy. Additionally you can define these values for every newsletter subscription form individually. You are able to set one or more group for every subscriber globally and add additional groups per subscription form.
Workflow
Default Storage Type
The default storage type is applied to every subscription form on your site if you don't override it. There are currently two storage types - 'local' and 'cleverreach'. 'local' is the default value - you need no entry in your app/config/enhavo.yml. If you want to use Clever Reach, put the following statement in your app/config/enhavo.yml and follow the instructions in the Clever Reach Configuration help file.
enhavo_newsletter:
storage:
default: cleverreach
Default Groups
You can associate subscribers with groups. This is mandatory for Clever Reach and optional for the local storage. To set default groups use the following example
enhavo_newsletter:
storage:
groups:
defaults:
- group1
- group2
- ..
Default Strategy
There are currently 3 different subscription strategies: notify, accept and double_opt_in. The default strategy is notify - you don't need to add the following statement if you want to use it. To set another default strategy use this statement
enhavo_newsletter:
strategy:
default: double_opt_in
Individual Form Settings
You are able to override the default settings for storage, strategy and groups for every individual form. Also you can define the type and template individually. Do it as follows
enhavo_newsletter:
forms:
<form_name>:
default_groups:
- 'code_of_group3'
storage:
type: local
strategy:
type: accept
type: enhavo_newsletter_subscribe
template: EnhavoNewsletterBundle:Subscriber:subscribe.html.twig
The form name "default" is already in use and defines type: enhavo_newsletter_subscribe and template: EnhavoNewsletterBundle:Subscriber:subscribe.html.twig as default - of course you can change these values, e.g.
enhavo_newsletter:
forms:
default:
type: <your_type>
template: <your_template>
Installation
$ composer require enhavo/newsletter-bundle
$ yarn add @enhavo/newsletter
// import statement
import NewsletterActionRegistryPackage from "@enhavo/newsletter/Action/ActionRegistryPackage";
// register the package
this.registerPackage(new NewsletterActionRegistryPackage(application));
Strategies
Enhavo has by default three different strategies to add subscriber to a storage. Here you will find how the strategy works and how to configure them.
Notify Strategy
The notify strategy just inform the admin and/or the subscriber that the subscriber was added to the storage.
enhavo_newsletter:
strategy:
settings:
notify:
<OPTIONS>
Option Description
notify A boolean if the subscriber should be notified
check_exists Check if email already exists in validation
subject Subscriber subject
translation_domain Subscriber Translation domain
template The subscriber template
from From email
admin_notify A boolean if the admin should be notified
admin_template Admin email template
admin_email Send to address
admin_subject Admin email subject
admin_translation_domain Admin translation domain
Accept Strategy
With accept strategy a user can sign up for a newsletter but need to be accepted by an admin before he will be added to the storage. After a user subscribe, the admin will receive an email with the subscriber information. The admin can add the user by clicking on a link in the email.
enhavo_newsletter:
strategy
settings:
accept:
<OPTIONS>
Option Description
notify A boolean if the subscriber should be notified
subject Subscriber subject
translation_domain Subscriber Translation domain
template The subscriber template
from From email
admin_template Admin email template
admin_email Send to address
admin_subject Admin email subject
admin_translation_domain Admin translation domain
activation_template Template for the activation site
Double Opt-In Strategy
With the double opt-in strategy, the subscriber need to confirm his entry before he will be added to the storage.
enhavo_newsletter:
strategy
settings:
double_opt_in:
<OPTIONS>
Option Description
notify A boolean if the subscriber should be notified
subject Subscriber subject
translation_domain Subscriber Translation domain
template The subscriber template
from From email
admin_notify A boolean if the admin should be notified
admin_template Admin email template
admin_email Send to address
admin_subject Admin email subject
admin_translation_domain Admin translation domain
activation_template Template for the activation site
Storages
There are currently two storage types - 'local' and 'clever_reach'. 'local' is the default value - you need no entry in your app/config/config.yml. If you want to use 'clever_reach', put the following statement in your app/config/config.yml and (follow the instructions for the credentials and group!):
Clever Reach
To use Clever Reach as your newsletter service, put the credentials in your app/config/config.yml as follows:
If you want to use Clever Reach as default storage, put the following statement in your app/config/enhavo.yml
enhavo_newsletter:
storage: 'cleverreach'
enhavo_newsletter:
storage:
default: cleverreach
settings:
cleverreach:
credentials:
client_id: '<YOUR_CLIENT_ID>'
login: '<YOUR_LOGIN>'
password: '<YOUR_PASSWORD>'
groups:
mapping:
<MAPPINGS>
group: '<YOUR_GROUP_ID>'
Mapping
A mapping contain the group name and the clever reach id
Group
You have to define a Clever Reach group, to which you add your new subscribers (find your group ID on the Clever Reach website). Define it as follows:
Clever Reach group mapping Clever Reach uses group IDs to identify a group in their system. To map the group name of the Enhavo Newsletter Bundle to a Clever Reach group use the following statement (keep in mind to rename group1 and group2 to your own enhavo group name)
enhavo_newsletter:
storage:
settings:
cleverreach:
groups:
mapping:
group1: <CR_GROUP_ID_1>
group2: <CR_GROUP_ID_2>
User defined modles
enhavo_newsletter:
newsletter:
test_receiver:
token: __TRACKING_TOKEN__
parameters:
firstname: Harry
lastname: Hirsch
token: __ID_TOKEN__
subscription:
local:
model: Enhavo\Bundle\NewsletterBundle\Model\CustomSubscriber
form:
class: Enhavo\Bundle\NewsletterBundle\Form\Type\CustomLocalSubscriberType
template: 'EnhavoNewsletterBundle:theme/resource/subscriber:custom-subscribe.html.twig'
options:
groups:
- default
- local
strategy:
type: notify
from: '%env(string:MAILER_FROM)%'
admin_email: '%env(string:MAILER_TO)%'
sender_name: '%env(string:MAILER_NAME)%'
check_exists: true
storage:
type: local
groups:
- default
cleverreach:
model: Enhavo\Bundle\NewsletterBundle\Model\CustomSubscriber
form:
class: Enhavo\Bundle\NewsletterBundle\Form\Type\CustomSubscriberType
template: 'EnhavoNewsletterBundle:theme/resource/subscriber:custom-subscribe.html.twig'
options:
groups:
- 1408629
strategy:
type: notify
from: '%env(string:MAILER_FROM)%'
admin_email: '%env(string:MAILER_TO)%'
sender_name: '%env(string:MAILER_NAME)%'
check_exists: true
storage:
type: cleverreach
client_secret: '%env(string:CLEVERREACH_CLIENT_SECRET)%'
client_id: '%env(string:CLEVERREACH_CLIENT_ID)%'
global_attributes:
firstname: firstName
lastname: lastName
groups:
- 1408629
mailchimp:
model: Enhavo\Bundle\NewsletterBundle\Model\CustomSubscriber
form:
class: Enhavo\Bundle\NewsletterBundle\Form\Type\CustomSubscriberType
template: 'EnhavoNewsletterBundle:theme/resource/subscriber:custom-subscribe.html.twig'
strategy:
type: notify
from: '%env(string:MAILER_FROM)%'
admin_email: '%env(string:MAILER_TO)%'
sender_name: '%env(string:MAILER_NAME)%'
storage:
type: mailchimp
url: '%env(string:MAILCHIMP_URL)%'
api_key: '%env(string:MAILCHIMP_API_KEY)%'
body_parameters:
merge_fields:
FNAME: firstName
LNAME: lastName
groups:
- c78b0465aa
resources:
local_subscriber:
classes:
factory: Enhavo\Bundle\NewsletterBundle\Factory\CustomLocalSubscriberFactory
model: Enhavo\Bundle\NewsletterBundle\Entity\CustomLocalSubscriber
form: Enhavo\Bundle\NewsletterBundle\Form\Type\CustomLocalSubscriberType
services:
enhavo_newsletter.factory.local_subscriber:
class: Enhavo\Bundle\NewsletterBundle\Factory\CustomLocalSubscriberFactory
arguments:
- '%enhavo_newsletter.model.local_subscriber.class%'
- '@enhavo_newsletter.repository.group'
enhavo_doctrine_extension:
metadata:
Enhavo\Bundle\NewsletterBundle\Entity\CustomLocalSubscriber:
extends: Enhavo\Bundle\NewsletterBundle\Entity\LocalSubscriber
discrName: 'app'
<?php
namespace App\Form\Type;
use Enhavo\Bundle\NewsletterBundle\Validator\Constraints\SubscriberExists;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CustomSubscriberType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstName', TextType::class);
$builder->add('lastName', TextType::class);
$builder->add('groups', CleverReachGroupType::class, [
'groups' => $options['groups'],
'subscription' => $options['subscription'],
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'constraints' => [
new SubscriberExists()
],
]);
}
public function getParent()
{
return SubscriberType::class;
}
public function getBlockPrefix()
{
return 'enhavo_newsletter_custom_subscriber';
}
}
<?php
namespace App\Form\Type;
use Enhavo\Bundle\NewsletterBundle\Entity\CustomLocalSubscriber;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CustomLocalSubscriberType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstName');
$builder->add('lastName');
$builder->add('groups', LocalGroupType::class, [
'groups' => $options['groups'],
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => CustomLocalSubscriber::class,
'translation_domain' => 'EnhavoNewsletterBundle',
));
}
public function getParent()
{
return SubscriberType::class;
}
public function getBlockPrefix()
{
return 'enhavo_newsletter_local_subscriber';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping">
<entity name="App\Entity\CustomLocalSubscriber"
repository-class="Enhavo\Bundle\NewsletterBundle\Repository\LocalSubscriberRepository">
<field name="lastName" nullable="true" />
<field name="firstName" nullable="true" />
</entity>
</doctrine-mapping>
<?php
namespace App\Model;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
class CustomSubscriber implements SubscriberInterface, GroupAwareInterface
{
/**
* @var string
*/
private $email;
/**
* @var \DateTime
*/
private $createdAt;
/**
* @var string
*/
private $subscription;
/**
* @var string|null
*/
private $confirmationToken;
/** @var string|null */
private $firstName;
/** @var string|null */
private $lastName;
/**
* @var GroupInterface[]
*/
private $groups = [];
/**
* @param string|null $email
*/
public function setEmail(?string $email): void
{
$this->email = $email;
}
/**
* @return string|null
*/
public function getEmail(): ?string
{
return $this->email;
}
/**
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* @param \DateTime $createdAt
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
}
/**
* @return string|null
*/
public function getSubscription(): ?string
{
return $this->subscription;
}
/**
* @param string|null $subscription
*/
public function setSubscription(?string $subscription): void
{
$this->subscription = $subscription;
}
/**
* @return string|null
*/
public function getConfirmationToken(): ?string
{
return $this->confirmationToken;
}
/**
* @param string|null $token
*/
public function setConfirmationToken(?string $token): void
{
$this->confirmationToken = $token;
}
public function __toString()
{
return $this->email;
}
/**
* @return string|null
*/
public function getFirstName(): ?string
{
return $this->firstName;
}
/**
* @param string|null $firstName
*/
public function setFirstName(?string $firstName): void
{
$this->firstName = $firstName;
}
/**
* @return string|null
*/
public function getLastName(): ?string
{
return $this->lastName;
}
/**
* @param string|null $lastName
*/
public function setLastName(?string $lastName): void
{
$this->lastName = $lastName;
}
/**
* @return GroupInterface[]
*/
public function getGroups(): array
{
return $this->groups;
}
/**
* @param GroupInterface $group
*/
public function addGroup(GroupInterface $group)
{
$this->groups[] = $group;
}
/**
* @param GroupInterface $group
*/
public function removeGroup(GroupInterface $group)
{
if (false !== $key = array_search($group, $this->groups, true)) {
array_splice($this->groups, $key, 1);
}
}
}
<?php
namespace App\Entity;
class CustomLocalSubscriber extends LocalSubscriber
{
/** @var string|null */
private $firstName;
/** @var string|null */
private $lastName;
/**
* @return string|null
*/
public function getFirstName(): ?string
{
return $this->firstName;
}
/**
* @param string|null $firstName
*/
public function setFirstName(?string $firstName): void
{
$this->firstName = $firstName;
}
/**
* @return string|null
*/
public function getLastName(): ?string
{
return $this->lastName;
}
/**
* @param string|null $lastName
*/
public function setLastName(?string $lastName): void
{
$this->lastName = $lastName;
}
}
<?php
namespace App\Factory;
use App\CustomLocalSubscriber;
use App\CustomSubscriber;
use Enhavo\Bundle\NewsletterBundle\Model\LocalSubscriberInterface;
use Enhavo\Bundle\NewsletterBundle\Model\SubscriberInterface;
class CustomLocalSubscriberFactory extends LocalSubscriberFactory
{
public function createFrom(SubscriberInterface $subscriber): LocalSubscriberInterface
{
/** @var CustomSubscriber $subscriber */
/** @var CustomLocalSubscriber $local */
$local = $this->createNew();
$local->setCreatedAt(new \DateTime());
$local->setEmail($subscriber->getEmail());
$local->setSubscription($subscriber->getSubscription());
$local->setFirstName($subscriber->getFirstName());
$local->setLastName($subscriber->getLastName());
return $local;
}
}