Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Referral Program Bundle Laravel Package

elcodi/referral-program-bundle

Elcodi bundle that adds a referral program to your e-commerce store: generate invite codes and links, track which customer referred each signup or order, and credit rewards to advocates. Integrates with Elcodi's user and order components.

View on GitHub
Deep Wiki
Context7

Getting Started

The package lives inside the elcodi/elcodi monorepo but is published to Packagist as a split package, so a legacy app pulls it directly:

composer require "elcodi/referral-program-bundle:^1.0"

You will also need elcodi/core, elocdi/user, elocdi/mailer and Doctrine ORM — the bundle is not standalone and its entities reference Elocodi\Component\User\Model\UserInterface. Register Elocodi\Bundle\ReferralProgramBundle\ElocodiReferralProgramBundle in app/AppKernel.php (Symfony 2.8/3.x era) and let the bundle's AcmeDemoBundle-style config block load. Budget an hour for dependency resolution alone: the constraint set targets PHP ~5.5–7.0 and Symfony 2.8/3.0, so on anything newer expect to pin symfony/* to ^3.4 at most.

Where to look first, in this order:

  1. Elocodi\Component\ReferralProgram\Services\ReferralProgramManager and its siblings (ReferralInvitationManager) — this is the actual API surface, everything else is wiring.
  2. Elocodi\Bundle\ReferralProgramBundle\DependencyInjection\Configuration — the whole configurable surface, ~20 lines.
  3. Resources/config/directors.yml — Elocodi's ObjectDirector pattern exposes elocodi.director.referral_hash and elocodi.director.referral_invitation; that is what you should inject, not the repositories.
  4. Resources/config/doctrine/ — mapping files, so you know what columns you are migrating.

First use case — the only one the bundle really solves: give a logged-in user a stable referral token and record who they invited. Render the token as ?ref=<hash> on your signup URL, capture it into the session in a request listener, and when the invited account is created, create a ReferralInvitation back to the referrer. Everything about what the referrer wins is your code, not the bundle's.

Implementation Patterns

Use directors, never repositories. Elocodi's core convention wraps factory + repository + object manager in one ObjectDirector. Follow it — bypassing it means your flush ordering diverges from the rest of the application.

class InviteController
{
    public function __construct(
        private ObjectDirector $referralHashDirector,
        private ObjectDirector $referralInvitationDirector,
        private ReferralProgramManager $referralProgramManager
    ) {}

    public function inviteAction(UserInterface $user, string $email)
    {
        $hash = $this->referralProgramManager->getReferralHashForUser($user); // creates if absent

        $invitation = $this->referralInvitationDirector->create();
        $invitation
            ->setReferralHash($hash)
            ->setEmail($email)
            ->setDate(new \DateTime());

        $this->referralInvitationDirector->save($invitation); // flushes

        return $this->render('ReferralProgramBundle:Invitation:sent.html.twig', ['hash' => $hash]);
    }
}

Hash capture belongs in a listener. Elocodi does not ship the "read ?ref= from the request" step; it only owns the hash entity. Add a kernel.request subscriber that stores $request->query->get('ref') in the session, and a user.registered/registration.success listener that looks up the ReferralHash by string and creates the invitation. This split is the intended seam and the reason the bundle looks thin.

Reward logic goes in an event subscriber on your own domain events. Listen to order.paid or your registration event, resolve the referrer through ReferralHash::getUser(), and issue a coupon through elocdi/coupon — reuse its CouponManager rather than writing discount logic. The bundle has no notion of "conversion", so don't look for ReferralLine semantics that don't exist in this package.

Override by service id, not by class. Every piece (elocodi.referral_program.generator.hash, the invitation mailer, the invitation template) is defined in Resources/config/*.yml. To change hash generation (e.g. UUID vs. random string), redeclare that service id in your app config with a compiler pass; that is the supported extension point, and it survives bundle upgrades better than __construct overrides.

Templates. The invitation email renders from ReferralProgramBundle:Email:invitation.html.twig. Override in app/Resources/ElocodiReferralProgramBundle/views/Email/ rather than editing vendor/, and keep the hash and user variables the bundle passes in — the mailer will fail silently (no exception, blank body) if you drop them.

Gotchas and Tips

  • This is EOL software. Last release 2016-02-15, archived repository, zero dependents on Packagist. Do not start new features on it. If you inherit an app already using it, treat the code as read-only and budget for extraction; the component is ~15 classes and is worth lifting into your own src/ if you still need referral hashes.
  • The hash is not guaranteed unique. Check your schema for the unique index on hash; if you write a custom generator, guard against collisions in a retry loop before save(), otherwise you get a UniqueConstraintViolationException on flush with no retry.
  • save() flushes. Directors call ObjectManager::flush() by default, so a controller that saves an invitation and then persists an order will interleave flushes. For batch work, wrap in $em->beginTransaction() or use the director's repository directly with your own flush().
  • Coupling to elocdi/user is hard. ReferralHash holds a UserInterface from that component. If your User is a FOSUserBundle entity, you need the Elocodi mapping/bridge; there is no adapter.
  • No reward/conversion state machine. There is no accepted/converted flag — you add it. A common trap is writing to ReferralInvitation::setDate() to mean "used"; add a migration and a real column instead.
  • Debug service wiring with: bin/console debug:container | grep referral and debug:container elocodi.director.referral_hash. Almost every "it doesn't save" bug is a missing director alias or the wrong entity manager in a multi-EM setup.
  • Check the mapping flavour before overriding. The bundle ships both annotation and YAML mappings under Resources/config/doctrine/; enabling both gives duplicate-mapping errors on cache warmup in Symfony 3.x. Pick one in config.yml and leave it.
  • Symfony 4+ will not boot this. The DI style, Definition::setFactory array syntax, and the app/AppKernel.php docs all assume pre-Flex. If you are modernising, shim the services manually — it is faster than trying to make the bundle install.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums