Installation Add the bundle via Composer:
composer require becklyn/rad-bundle
Register the bundle in config/bundles.php:
return [
// ...
Becklyn\RadBundle\BecklynRadBundle::class => ['all' => true],
];
First Use Case: AJAX Response
Inject AjaxResponseBuilder into a controller:
use Becklyn\RadBundle\AjaxResponseBuilder;
class MyController extends AbstractController
{
public function ajaxAction(AjaxResponseBuilder $builder): Response
{
return $builder
->ok()
->data(['key' => 'value'])
->build();
}
}
Use the mojave client to handle responses in TypeScript:
const response = await fetch('/ajax-endpoint');
const result = await mojave.ajaxResponse(response);
if (!result.ok) {
// Handle error (e.g., `result.status` = "invalid-id")
}
Form Extensions
Extend a form type with RadFormExtension:
use Becklyn\RadBundle\Form\Extension\RadFormExtension;
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addExtension(new RadFormExtension());
}
}
Consistent Response Handling
Use AjaxResponseBuilder for all AJAX endpoints to enforce the protocol:
return $builder
->ok()
->status('custom-status')
->data(['items' => $items])
->message(['text' => 'Success!', 'impact' => 'positive'])
->build();
Frontend Integration
Pair with mojave for TypeScript:
// Handle redirects
if (result.redirect) {
window.location.href = result.redirect;
}
// Show toasts
if (result.message) {
mojave.toast(result.message);
}
Error Handling
Return non-ok responses with descriptive status:
return $builder
->fail()
->status('invalid-data')
->data(['errors' => $errors])
->build();
Dynamic Field Validation
Use RadFormExtension to add client-side validation:
$builder->add('email', EmailType::class, [
'constraints' => [new NotBlank(), new Email()],
]);
// Automatically adds `data-rad-constraints` attributes.
Custom JavaScript Events
Attach events via RadFormExtension:
$builder->addExtension(new RadFormExtension([
'events' => [
'submit' => 'myCustomSubmitHandler',
],
]));
Nested Form Support Extend nested forms for AJAX-driven updates:
$builder->add('address', AddressType::class, [
'entry_options' => ['rad' => ['ajax' => true]],
]);
Symfony UX Turbo/Stimulus Combine with Symfony UX for progressive enhancement:
// Controller
return $builder
->ok()
->data(['html' => $this->renderView('partial.html.twig')])
->build();
// Stimulus controller
connect() {
this.fetch('/ajax-endpoint').then(response => {
this.element.innerHTML = response.data.html;
});
}
API Platform
Override serializeContext to inject AJAX metadata:
public function serializeContext(Operation $operation, array $uriVariables = [], array $context = []): array
{
$context['groups'] = ['ajax'];
return parent::serializeContext($operation, $uriVariables, $context);
}
Custom Twig Functions Extend Twig with AJAX-aware helpers:
// services.yaml
Becklyn\RadBundle\Twig\AjaxExtension:
tags: ['twig.extension']
Deprecation Warning
becklyn/rad v8+.rad package for long-term support.TypeScript Mismatch
mojave is updated to match the AjaxResponse interface in the bundle.Form Extension Conflicts
RadFormExtension may override existing form behaviors (e.g., CSRF tokens).AJAX Redirects
redirect field) are client-side only. Ensure your frontend handles them gracefully:
if (result.redirect && !result.redirect.startsWith('http')) {
// Assume relative path
window.location.href = result.redirect;
}
Response Validation
Use var_dump($builder->getResponse()) to inspect the raw response before sending.
Frontend Errors Check the browser’s Network tab for malformed AJAX responses. Validate:
ok is a boolean.status is a non-empty string.data is serializable (no circular references).Form Events Debug JavaScript events with:
document.addEventListener('rad:submit', (e) => {
console.log('Form submitted:', e.detail);
});
Default AJAX Protocol
ok: false.Twig Autoescape Disable autoescaping for AJAX-returned HTML:
{{ render(controller('AppController::ajaxPartial'))|raw }}
CSRF Protection
CSRF_TOKEN in headers. Use mojave to auto-include it:
mojave.fetch('/submit', {
method: 'POST',
headers: { 'X-Requested-With': 'XMLHttpRequest' },
});
Custom Response Builders
Extend AjaxResponseBuilder:
class CustomResponseBuilder extends AjaxResponseBuilder
{
public function withCustomField($value): self
{
$this->response['custom'] = $value;
return $this;
}
}
Form Event Extensions
Add custom events to RadFormExtension:
$builder->addExtension(new RadFormExtension([
'events' => [
'change:email' => 'validateEmail',
],
]));
Twig Globals Override Twig globals for AJAX-specific filters:
// services.yaml
Becklyn\RadBundle\Twig\AjaxRuntime:
tags: ['twig.runtime']
Serializer Normalization Customize serialization for AJAX responses:
// config/packages/serializer.yaml
Becklyn\RadBundle\Serializer\AjaxNormalizer:
tags: [serializer.normalizer]
arguments: ['@serializer.mapping.class_metadata_factory']
How can I help you explore Laravel packages today?