Installation
composer require blackboxcode/pando-base-bundle
Add to config/bundles.php:
BlackBoxCode\PandoBaseBundle\PandoBaseBundle::class => ['all' => true],
First Use Case: Base Controller
Extend PandoBaseBundle\Controller\BaseController for CRUD operations:
use BlackBoxCode\PandoBaseBundle\Controller\BaseController;
class ProductController extends BaseController
{
protected $entityClass = Product::class;
protected $serializerGroups = ['default', 'api'];
}
index, store, show, update, destroy).Key Files to Explore
src/Controller/BaseController.php: Core logic for CRUD.config/packages/pando_base.yaml: Default configuration.Resources/config/routing.yaml: Default routes (extendable).BaseController to get pre-built routes.
# config/routes.yaml
pando_base:
resource: "@PandoBaseBundle/Resources/config/routing.yaml"
prefix: /api
createEntity() or updateEntity():
protected function createEntity(Request $request): Product
{
$data = $request->request->all();
$data['slug'] = Str::slug($data['name']); // Custom logic
return parent::createEntity($request);
}
Serialize annotations:
/**
* @ORM\Entity
* @ApiResource(
* normalizationContext={"groups": {"default"}},
* itemOperations={...}
* )
*/
class Product {}
getSerializerGroups() in your controller for dynamic groups.use Symfony\Component\Validator\Constraints as Assert;
/**
* @Assert\NotBlank
* @Assert\Length(min=3)
*/
private $name;
BaseController via handleValidationErrors().prePersist, postRemove):
use BlackBoxCode\PandoBaseBundle\Event\BaseEntityEvent;
public function prePersist(BaseEntityEvent $event): void
{
$event->getEntity()->setCreatedAt(new \DateTime());
}
# config/services.yaml
BlackBoxCode\PandoBaseBundle\EventListener\YourListener:
tags:
- { name: kernel.event_listener, event: pando_base.pre_persist, method: onPrePersist }
# config/api_platform.yaml
resources:
App\Entity\Product:
collectionOperations:
- GET
- POST
itemOperations:
- GET
- PUT
- DELETE
normalization_context:
groups: [default, 'api']
Route Conflicts
pando_base_product:
resource: "@PandoBaseBundle/Resources/config/routing.yaml"
prefix: /api/v1/products
Serialization Groups
@ApiResource(normalizationContext={"groups": {"default"}})
Event Priority
prePersist run before Doctrine’s lifecycle callbacks. Use postPersist for post-save logic.Doctrine vs. API Platform
ApiResource annotations in the bundle’s base controller.Enable Debug Mode: Add to config/packages/dev/pando_base.yaml:
debug: true
Logs SQL queries and event dispatches.
Check Events: Dump dispatched events in a listener:
public function onPrePersist(BaseEntityEvent $event): void
{
\dump($event->getEntity());
}
Custom Controllers
BaseController for domain-specific logic:
class AdminProductController extends BaseController
{
protected $entityClass = Product::class;
protected $permission = 'ROLE_ADMIN';
}
Dynamic Routes
getRoutePrefix() for dynamic segments:
protected function getRoutePrefix(): string
{
return '/api/v1/' . $this->getEntityName();
}
Custom Serializers
services.yaml:
App\Serializer\CustomProductSerializer:
tags: [serializer.normalizer]
arguments:
- '@doctrine.orm.entity_manager'
Bulk Operations
BaseController::bulkAction() for batch updates/deletes:
public function bulkDelete(Request $request)
{
$this->bulkAction($request, 'DELETE');
}
How can I help you explore Laravel packages today?