c0ntax/parsley-bundle
Symfony bundle that maps Symfony Form constraints and entity annotations to Parsley.js data-parsley-* attributes for client-side validation. Includes basic configuration (enable/trigger) and supports Email, Length, Pattern, Min/Max, Required, and Range.
Install the Bundle:
composer require c0ntax/parsley-bundle
Add to config/bundles.php (Symfony) or manually register in AppKernel.php if not using Flex.
Enable Parsley.js:
Include Parsley.js in your layout (e.g., base.html.twig):
<script src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.js"></script>
Basic Configuration (optional):
# config/packages/c0ntax_parsley.yaml
c0ntax_parsley:
enabled: true
field:
trigger: "blur" # Default: "focusout"
First Use Case:
Add a Symfony constraint to a form field (e.g., EmailType):
// src/Form/YourFormType.php
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('email', EmailType::class, [
'constraints' => [new Assert\Email()]
]);
}
The bundle auto-converts this to data-parsley-type="email" in the rendered HTML.
Length, Email) in forms or entities.data-parsley-* attributes.// Entity with annotation
/**
* @Assert\Length(min=5, max=20)
*/
private $username;
Renders as:
<input type="text" data-parsley-minlength="5" data-parsley-maxlength="20">
parsleys option for Parsley-specific rules not tied to Symfony:
$builder->add('bio', TextType::class, [
'parsleys' => [
new \C0ntax\ParsleyBundle\Parsleys\Directive\Field\Constraint\MinLength(100, 'Bio must be at least 100 characters.')
]
]);
$builder->add('age', IntegerType::class, [
'parsleys' => [
new \C0ntax\ParsleyBundle\Parsleys\Directive\Field\ConstraintErrorMessage(
\C0ntax\ParsleyBundle\Parsleys\Directive\Field\Constraint\Range::class,
'Age must be between {{ min }} and {{ max }}.'
)
]
]);
Regex) from client-side validation:
$builder->add('customField', TextType::class, [
'constraints' => [new Assert\Regex('/pattern/')],
'parsleys' => [new \C0ntax\ParsleyBundle\Parsleys\Directive\Field\RemoveSymfonyConstraint(Assert\Regex::class)]
]);
$builder->add('username', TextType::class, [
'parsley_trigger' => 'keyup' // Overrides global `trigger` config
]);
ConstraintErrorMessage to align error messages:
$builder->add('externalField', TextType::class, [
'parsleys' => [
new \C0ntax\ParsleyBundle\Parsleys\Directive\Field\ConstraintErrorMessage(
\C0ntax\ParsleyBundle\Parsleys\Directive\Field\Constraint\Email::class,
'Please enter a valid email address.'
)
]
]);
DirectiveInterface for custom Parsley attributes:
use C0ntax\ParsleyBundle\Parsleys\Directive\DirectiveInterface;
class CustomDirective implements DirectiveInterface {
public function getAttributes(): array {
return ['data-parsley-custom' => 'value'];
}
}
$builder->add('field', TextType::class, [
'parsleys' => [new CustomDirective()]
]);
Unsupported Constraints:
Callback or Expression constraints).RemoveSymfonyConstraint for unsupported rules and add custom Parsley validations.Error Message Mismatches:
{{ limit }}).ConstraintErrorMessage (see Implementation Patterns).Parsley.js Not Loaded:
parsley.js errors.Trigger Conflicts:
trigger settings (e.g., focusout) may not suit all fields (e.g., keyup for search boxes).parsley_trigger per field or adjust global config.Dynamic Forms:
Parsley.update() or reinitialize Parsley on dynamic content changes.Symfony 5+ Deprecations:
Constraint class renames in Symfony 5).Inspect Rendered HTML:
Check if data-parsley-* attributes are injected:
<input data-parsley-required="true" data-parsley-trigger="focusout">
Console Logs: Enable Parsley’s debug mode:
window.Parsley.addValidator({
name: 'debug',
fn: function() { console.log('Parsley initialized'); return true; }
});
Disable Bundle Temporarily:
Set enabled: false in config to isolate issues:
c0ntax_parsley:
enabled: false
Bundle Initialization:
The bundle runs during form rendering, which may add slight overhead for complex forms.
Tip: Disable for non-critical forms or use enabled: false in config.
Parsley.js Overhead: Parsley adds ~10KB to your bundle. For minimal forms, consider inlining Parsley or lazy-loading it.
Custom Validators:
Extend Parsley’s validators via JavaScript and map them to Symfony constraints using DirectiveInterface.
Event Listeners:
Hook into form events (e.g., PRE_SET_DATA) to dynamically modify Parsley rules:
// src/EventListener/ParsleyListener.php
public function onPreSetData(FormEvent $event) {
$form = $event->getForm();
if ($form->getName() === 'dynamic_form') {
$form->add('field', TextType::class, [
'parsleys' => [new CustomDirective()]
]);
}
}
Twig Integration: Access Parsley attributes in Twig for custom templates:
{{ form_widget(form.field, {
'attr': {
'data-parsley-custom': '{{ form.vars.data.parsley_custom }}'
}
}) }}
Global vs. Field-Level Triggers:
c0ntax_parsley.field.trigger (e.g., focusout).'parsley_trigger' => 'keyup' in form options.Disabling for Specific Fields:
Use RemoveParsleyDirective to exclude Parsley from
How can I help you explore Laravel packages today?