Skip to content

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

image

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.

yaml
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

yaml
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

yaml
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

yaml
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.

yaml
enhavo_newsletter:
    forms:
        default:
            type: <your_type>
            template: <your_template>

Installation

bash
$ composer require enhavo/newsletter-bundle
bash
$ yarn add @enhavo/newsletter
ts
// 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.

yaml
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.

yaml
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.

yaml
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

yaml
enhavo_newsletter:
    storage: 'cleverreach'
yaml
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)

yaml
enhavo_newsletter:
    storage:
        settings:
            cleverreach:
                groups:
                    mapping:
                        group1: <CR_GROUP_ID_1>
                        group2: <CR_GROUP_ID_2>

User defined modles

yaml
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
<?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
<?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
<?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
<?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
<?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
<?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;
    }

}