activpik/feedback-bundle
Symfony 2 bundle to add a feedback badge and submission form to your app. Configure a feedback source service (e.g., Redmine JSON API), register routes/controllers, include the provided CSS/JS, and embed the badge Twig template in your views.
Installation
Add the bundle via Composer (note: dev-trunk is unstable; prefer a stable release if available):
composer require activpik/feedback-bundle:dev-trunk
Register the Bundle
Add to config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 2/3):
new Activpik\Feedback\ActivpikFeedbackBundle(),
Configure Services
Define a feedback source (e.g., Redmine) in config/services.yaml:
services:
activpik.feedback_source:
class: Activpik\FeedbackBundle\Vendor\RedmineFeedback\RedmineFeedbackSource
arguments:
- "https://urltoredmine/redmine/projects/myproject/issues.json"
- "%env(REDMINE_API_KEY)%"
- "%env(REDMINE_PROJECT_ID)%"
- "Priority"
Route the Bundle
Add to config/routes.yaml (Symfony 4+):
activpik_feedback:
resource: "@ActivpikFeedbackBundle/Controller/"
type: annotation
prefix: /
First Use Case: Display a Feedback Badge In a controller:
use Activpik\FeedbackBundle\Form\FeedbackType;
public function showAction()
{
$feedbackType = new FeedbackType();
return $this->render('page.html.twig', [
'feedback_form' => $this->createForm($feedbackType)->createView()
]);
}
In Twig (templates/page.html.twig):
{{ include('ActivpikFeedbackBundle:Feedback:badge.html.twig') }}
Integrating Feedback into Existing Pages
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('bundles/activpikfeedback/css/feedback.css') }}">
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('bundles/activpikfeedback/js/feedback.js') }}"></script>
{% endblock %}
{{ include('ActivpikFeedbackBundle:Feedback:badge.html.twig') }}
Handling Feedback Submissions
ActivpikFeedbackBundle:Feedback:submit. Extend the controller to process submissions (e.g., log to Redmine):
// src/Controller/FeedbackController.php
namespace App\Controller;
use Activpik\FeedbackBundle\Controller\FeedbackController as BaseFeedbackController;
class FeedbackController extends BaseFeedbackController
{
public function submitAction()
{
$data = $this->getRequest()->request->all();
// Custom logic (e.g., validate, enrich, or forward to Redmine)
return parent::submitAction();
}
}
Dynamic Feedback Sources
RedmineFeedbackSource for other backends (e.g., Jira, custom API):
# config/services.yaml
services:
activpik.feedback_source.custom:
class: App\Service\CustomFeedbackSource
arguments: ["%env(CUSTOM_API_URL)%"]
tags: ['activpik.feedback_source']
Symfony Forms Integration
Use the FeedbackType form to standardize feedback fields (e.g., name, email, message). Extend it for custom fields:
use Activpik\FeedbackBundle\Form\FeedbackType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class ExtendedFeedbackType extends FeedbackType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->add('custom_field', TextType::class);
}
}
Twig Embedding For reusable feedback modals/dialogs, create a custom Twig extension or macro:
{% macro feedbackModal(feedbackForm) %}
<div class="feedback-modal">
{{ form_start(feedbackForm) }}
{{ form_widget(feedbackForm) }}
<button type="submit">Send</button>
{{ form_end(feedbackForm) }}
</div>
{% endmacro %}
Asset Management
Override bundle assets by copying vendor/activpik/feedback-bundle/Resources/public/ to public/bundles/activpikfeedback/ and customizing CSS/JS.
Unstable dev-trunk
Missing Documentation
src/Controller/FeedbackController.php.src/Form/FeedbackType.php.submitAction() flow.Hardcoded Asset Paths
bundles/activpikfeedback/. If using Symfony Flex or custom asset paths, override them:
<link rel="stylesheet" href="{{ asset('custom-path/feedback.css') }}">
Redmine Dependency
RedmineFeedbackSource tightly couples the bundle to Redmine. If using another issue tracker, implement a new source class and tag it:
tags: ['activpik.feedback_source']
Form Validation Errors
FeedbackType constraints and ensure your Twig form includes error rendering:
{{ form_errors(feedback_form) }}
JavaScript Errors
feedback.js loads after jQuery (if dependent). Use browser dev tools to check for:
$ (jQuery) or console errors.Routing Conflicts
prefix: / in routing.yml may clash with other routes. Use a unique prefix:
prefix: /feedback
Custom Feedback Sources
Activpik\FeedbackBundle\Feedback\FeedbackSourceInterface:
class MyFeedbackSource implements FeedbackSourceInterface
{
public function submit(array $data): bool
{
// Custom logic (e.g., API call)
return true;
}
}
activpik.feedback_source tag.Event Listeners
// src/EventListener/FeedbackListener.php
use Symfony\Component\HttpKernel\Event\RequestEvent;
class FeedbackListener
{
public function onKernelRequest(RequestEvent $event)
{
if ($event->isMainRequest() && $event->getRequest()->get('_route') === 'activpik_feedback_submit') {
// Log or modify request data
}
}
}
Register in services.yaml:
services:
App\EventListener\FeedbackListener:
tags: ['kernel.event_listener', { event: 'kernel.request', method: 'onKernelRequest' }]
Twig Overrides
vendor/activpik/feedback-bundle/Resources/views/ to templates/bundles/ActivpikFeedbackBundle/ and modifying:
Feedback/badge.html.twig (styling/positioning).Feedback/submit.html.twig (custom submission logic).Environment Variables
The example uses %env(REDMINE_API_KEY)%, but the bundle may not support Symfony’s env vars by default. Use a parameter:
# config/packages/parameters.yaml
parameters:
redmine.api_key: '%env(REDMINE_API_KEY)%'
Then inject the parameter into the service.
Priority Argument
The Priority argument in RedmineFeedbackSource is hardcoded. Extend the class to make it configurable:
class ConfigurableRedmineFeedbackSource extends RedmineFeedbackSource
{
public function __construct(string $url, string $apiKey, string $projectId, ?string $defaultPriority = null)
{
$this->defaultPriority = $defaultPriority;
parent::__construct($url, $apiKey, $projectId, $defaultPriority);
}
}
How can I help you explore Laravel packages today?