elcodi/referral-program
Referral program add-on for Elcodi e-commerce that helps you create and manage referral codes, track invites and conversions, and reward customers for bringing in new buyers.
Before writing a line of code, confirm what you are actually adopting. elcodi/referral-program is one component inside the archived elcodi/elcodi monorepo — last tagged release 2016-02-15, EOL, zero dependents, built for Symfony 2.x / PHP 5.5–7.0. There is no maintainer, no security process, and no upgrade path. Treat this as reference material you vendor or fork, not a composer require you bolt onto a modern app.
Minimal evaluation steps:
git clone https://github.com/elcodi/elcodi — do not install via Packagist into a Symfony 5/6/7 project; the DI extension, XML service definitions, and Doctrine mapping conventions will fail to load.src/Elcodi/Component/ReferralProgram/Entity/ — the domain model (program, invitation, referral hash/token, and the rule that binds a program to a coupon).src/Elcodi/Component/ReferralProgram/Resources/config/doctrine/ — the XML mappings; this is the real specification of the schema.src/Elcodi/Component/ReferralProgram/Services/ — the managers (*Manager.php) that own the lifecycle.src/Elcodi/Component/ReferralProgram/Events/ + EventListener/ — the extension surface.elcodi/coupon and elcodi/user, since the referral component depends on both and inherits their base entity traits (id, enabled, createdAt, updatedAt).The canonical first use case is invite-a-friend: an existing user gets a referral hash/link, a new visitor registers through it, an invitation record moves through its states, and a coupon is issued on conversion. Build that end-to-end in a scratch project (or a test harness around the entities) before deciding whether the abstraction is worth porting.
Programs as seeded configuration, not admin-created rows. A referral program is a small aggregate: an identity, a set of rules, and a coupon linkage. Create them in fixtures or a Doctrine migration with stable slugs, and reference programs by slug in application code. Letting merchandisers create programs freely tends to produce orphaned invitations and unclaimable coupons.
The hash is the public identifier. Never expose user IDs in referral URLs. Generate the referral hash through the component's manager on user creation (or lazily on first "invite" click), persist it, and resolve inbound traffic with a single lookup service of your own that wraps the component's repository. Keep that resolver behind an interface — it's the one piece you will definitely rewrite.
Model the invitation lifecycle explicitly. The component gives you states around invited → registered → converted/rewarded. Push transitions through a thin application service rather than mutating entities from controllers, and make each transition idempotent: registerInvitation($hash, $newUser) should no-op if an invitation already exists for that pair. Duplicate invitation rows are the number one production bug in referral systems.
Issue coupons on the conversion event, not on registration. Listen for the referral conversion/reward event and call the elcodi/coupon coupon manager there. Store the issued coupon code on the invitation row so you can reconcile, revoke, and report. If you're porting the model, keep this as a domain event (ReferralConverted) so coupon issuance stays in a listener — it's the cleanest seam in the whole component.
Keep business logic in listeners/subscribers. The component is dispatcher-driven and was designed to be extended without editing it. If you fork it, preserve that: a ReferralProgramListener that reacts to your app's UserRegistered event, resolves the hash, and creates the invitation is preferable to subclassing a manager (which drags you into constructor signature churn across the Elcodi graph).
For modern Symfony, port — don't install. Copy the entity classes and XML mappings into your own namespace, re-express the mappings as attributes or YAML, and reimplement the two or three managers you actually need (hash generation, invitation creation, reward issuance). The domain is genuinely small; a faithful reimplementation is typically 300–500 lines and removes the elcodi/coupon, elcodi/cart, and elcodi/product transitive baggage entirely.
Reporting is a simple group-by. Conversion rate per program = invitations with a reward / invitations created, grouped by program ID over a date window. Build it as a query on your own invitation table; don't expect the component to ship analytics.
elcodi/coupon and elcodi/user, and in practice most of the Elcodi e-commerce stack. Audit composer why before assuming it's lightweight — it is not.Resources/config/doctrine to your doctrine.orm.mappings config explicitly, or you'll get "table not found" errors that look like cache problems.dump() calls liberally around the manager methods rather than guessing at intended behavior.How can I help you explore Laravel packages today?