kunstmaan/voting-bundle
KunstmaanVotingBundle adds reusable voting and rating features to Symfony applications, including entities, services, and templates for casting and tallying votes. Lightweight but low-maintenance; verify compatibility before use.
Reality check first. Last tag is April 2014, it targets Symfony 2.x, and it has zero dependents. Do not plan a new build on top of it — read it as a reference implementation, or fork it under your own vendor name so you control the PHP/Symfony compatibility surface.
Minimal install steps (Symfony 2.3–2.8 era):
composer require kunstmaan/voting-bundle:dev-master
// app/AppKernel.php
new Kunstmaan\VotingBundle\KunstmaanVotingBundle(),
# app/config/routing.yml
kunstmaan_voting:
resource: "@KunstmaanVotingBundle/Resources/config/routing.yml"
prefix: /voting
Then app/console doctrine:schema:update --dump-sql and read the diff before you run it — the bundle ships entities, not migrations.
Where to look first, in this order:
Entity/ — the vote classes (a base vote plus per-network subclasses). This tells you what a "vote" actually is: reference string, IP, timestamp, and the network-specific payload.Resources/config/ — service IDs and the routing file for the vote endpoint. Symfony 2 container IDs, so no autowiring; you'll be calling $this->get('kunstmaan_voting.…').Service/ — the class that actually registers a vote and dispatches the event. This is the only part you'll call from your own code.Controller/ — the HTTP entry point the frontend JS hits.First use case: a campaign page where a user clicks "Like us" and you want a counted, auditable vote. Give the campaign item a stable reference string (e.g. campaign-42 or campaign-42:user-1234), let the frontend POST to the bundle's endpoint, and query the repository for the count. That's the whole value proposition: you get the ledger for free, not the UI.
Reference strings are the primary key of your counters. Everything hangs off a free-text reference. Adopt a convention on day one — {campaign}:{item} for aggregate counts, {campaign}:{item}:{userHash} if you need per-user dedup — and never change it mid-campaign, because there's no foreign key to anything.
Register votes through the service, not the entity. Persisting a FacebookLikeVote directly skips whatever validation/expiry/event logic the service does. From your own controller:
$voteService = $this->get('kunstmaan_voting.service.vote');
// inspect Service/VoteService.php for the exact signature — it takes
// the request (for IP/session) plus a vote type and reference
$vote = $voteService->registerVote($request, $reference, 'facebook_like');
Wrap the call in try/catch: the bundle throws typed exceptions for duplicate votes, expired campaigns, and unknown vote types. Those exceptions are the API — map them to 409/410/400 responses rather than letting them 500.
Two integration styles for the frontend. Either let the bundle's controller own the route and have your JS POST to /voting/... with the CSRF token, or hide the route and call the service from your own JSON endpoint so you control auth, rate limiting, and the response shape. The second is what most production sites end up doing.
React to votes with listeners, not modifications. Find the event the service dispatches (look for EventDispatcher / VoteEvents in Service/) and subscribe to it. That's the clean extension point for sending a confirmation email, incrementing a denormalised counter, or writing to your own analytics table. It survives a fork.
Campaign deadlines: enforce twice. The bundle checks expiry at registration time. That doesn't stop a cron from continuing to send reminders or a page from rendering a live button. Keep your own Campaign entity with startsAt/endsAt and check it in your controller too.
Irregularity reporting is a query, not a feature. There's no admin UI. Build a small reporting command that groups the vote repository by IP and by reference over a time window; anything with an implausible per-IP rate is your fraud signal.
Extending vote types: subclass the base vote entity if Doctrine inheritance is configured, otherwise add a nullable column via your own entity mapping. Check DiscriminatorMap in the base entity before you assume.
symfony/symfony ~2.x and PHP 5.x-era code. To use it at all today, fork it, loosen composer.json, and fix deprecated calls; don't try to composer require it into a Symfony 5/6 app.doctrine:schema:update will happily drop or rename columns on a table with production votes. Diff first, hand-write the migration.$request->getClientIp() is the balancer unless trusted_proxies is configured — every vote then looks like it came from one address, and your fraud report is useless.(reference, ip) or (reference, user_id) yourself if one vote per user is the rule.$this->get('kunstmaan_voting.…'), no autowiring, service definitions in XML/YAML. If you fork, don't rename IDs — every site using the bundle already references them.app/console command that dumps count-by-reference before you believe a bug report; nine times out of ten the mismatch is a changed reference convention, not a lost vote.Request, and assert on the exception types. Don't write functional tests against the bundle's controller — you'll be rewriting them the moment you take over the route.How can I help you explore Laravel packages today?