Install via Composer (Symfony bundle, but core components can be adapted):
composer require becklyn/rad-bundle
Note: Since this is a Symfony bundle, use only the core classes (AjaxResponseBuilder, SimpleEntitySearchHelper, etc.) via direct dependency injection or facades.
Replicate the AJAX Protocol in Laravel:
Create a base controller trait to mirror BaseController::ajaxResponse():
// app/Traits/AjaxResponseTrait.php
use Illuminate\Http\JsonResponse;
trait AjaxResponseTrait
{
protected function ajaxResponse(
bool $ok,
string $status,
mixed $data = null,
?string $redirect = null,
?array $message = null
): JsonResponse {
return response()->json([
'ok' => $ok,
'status' => $status,
'data' => $data,
'redirect' => $redirect,
'message' => $message,
]);
}
}
First Use Case: AJAX Endpoint Extend a controller to use the trait:
// app/Http/Controllers/ExampleController.php
use App\Traits\AjaxResponseTrait;
class ExampleController extends Controller
{
use AjaxResponseTrait;
public function fetchData()
{
try {
$data = Model::all();
return $this->ajaxResponse(true, 'success', $data->toArray());
} catch (\Exception $e) {
return $this->ajaxResponse(false, 'error', null, null, [
'text' => $e->getMessage(),
'impact' => 'negative',
]);
}
}
}
Frontend Integration:
Use the mojave client or adapt the TypeScript interface:
// types/ajax-response.d.ts
interface AjaxResponse {
ok: boolean;
status: string;
data: Record<string, any> | any[];
redirect?: string;
message?: {
text: string;
impact: 'positive' | 'negative' | 'neutral';
action?: { label: string; url: string };
};
}
Request Handling:
Use AjaxResponseTrait in controllers to standardize responses. Example:
public function create(Request $request)
{
$validated = $request->validate([...]);
$resource = Model::create($validated);
return $this->ajaxResponse(
true,
'created',
$resource,
route('resource.show', $resource),
['text' => 'Resource created successfully', 'impact' => 'positive']
);
}
Error Handling: Catch exceptions and return structured errors:
catch (ValidationException $e) {
return $this->ajaxResponse(
false,
'validation_error',
null,
null,
['text' => 'Validation failed', 'impact' => 'negative']
);
}
Redirects:
Use the redirect field for SPA-like navigation:
return $this->ajaxResponse(
true,
'redirect_needed',
null,
route('dashboard')
);
Collection Fields:
Adapt Symfony’s CollectionType behavior in Laravel using collective/html or custom views:
// In a FormRequest or Controller
public function buildForm(BuilderInterface $builder, array $options)
{
$builder->add('tags', CollectionType::class, [
'entry_type' => TextType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'entry_options' => [
'label' => false,
],
'empty_message' => 'No tags added yet',
'entry_add_label' => 'Add Tag',
'entry_remove_label' => 'Remove',
]);
}
Note: Laravel lacks native CollectionType, so use packages like laravelcollective/html or build custom logic.
JSON Search:
Use SimpleEntitySearchHelper for dynamic queries (adapt for Laravel/Eloquent):
use Becklyn\RadBundle\Helper\SimpleEntitySearchHelper;
class PostRepository
{
public function search(array $criteria)
{
$query = Post::query();
$helper = new SimpleEntitySearchHelper();
$helper->applyJsonSearch($query, $criteria, 'metadata');
return $query->get();
}
}
Deferred Routes/Translations: Use for lazy-loading routes or translations:
use Becklyn\RadBundle\Deferred\DeferredRoute;
$route = new DeferredRoute('resource.show', ['id' => $id]);
if ($route->isValidValue()) {
$url = $route->generateValue();
}
Deferred Forms:
For dynamic form generation (Symfony-specific; replicate with Laravel’s FormBuilder or Livewire).
Pagination):
class PaginationHelper
{
public static function calculateOffset(int $page, int $perPage): int
{
return ($page - 1) * $perPage;
}
}
Symfony Dependencies:
FormBuilder, EventDispatcher, or DependencyInjection won’t work directly in Laravel. Use only the protocol/utility classes (AjaxResponseBuilder, SimpleEntitySearchHelper, etc.).symfony/form) and wrap them in Laravel facades.TypeScript Interface Mismatch:
AjaxResponse interface assumes specific fields. Deviations (e.g., Laravel’s default JSON structure) will break frontend logic.mojave to handle both formats.Form Extensions:
Form facade doesn’t support Symfony’s CollectionType out of the box. Custom views or packages like laravelcollective/html are needed.Replicator or DynamicFields packages for similar functionality.Doctrine vs. Eloquent:
SimpleEntitySearchHelper uses Doctrine’s JSON_SEARCH(). For Eloquent, use raw queries or packages like spatie/laravel-query-builder.Deprecated Features:
BundleExtension (deprecated in v7.13.3). Use standalone classes instead.AJAX Responses:
ok and status fields in frontend logs. Example:
const response = await fetch('/api/data');
const data = await response.json();
if (!data.ok) {
console.error(`Status: ${data.status}`, data.message);
}
Form Errors:
trans('validation.custom_field', [], 'messages');
JSON Paths:
SimpleEntitySearchHelper::applyJsonSearch() requires MySQL’s JSON_SEARCH(). For PostgreSQL, use jsonb_path_query or adapt the helper.Custom AJAX Protocols:
AjaxResponseTrait to add fields (e.g., meta for pagination):
protected function ajaxResponse(...): JsonResponse
{
$response = parent::ajaxResponse(...);
$response->setData([
'meta' => ['pagination' => [...]],
...$response->getData()
]);
return $response;
}
Frontend Adaptors:
mojave alternative (e.g., laravel-mojave) to handle quirks like CSRF tokens or Laravel’s auth system.Form Helpers:
LaravelCollectionType) that mirror Symfony’s behavior using livewire or inertiajs.Translation Domains:
validators domain. In Laravel, override in config/translation.php:
'domains' => [
'validators' => LaravelTranslationDomain::class,
],
CSRF Protection:
_token or use VerifyCsrfToken middleware.PHP Version:
How can I help you explore Laravel packages today?