conduction/commongroundbundle
Symfony/API Platform bundle adding VNG Common Ground features for Dutch government apps: VNG API Standard support, BSN checks, KVK lookups, and resource lifecycle events (create/update/save/delete). Requires PHP 7.1+.
Installation
composer require conduction/commongroundbundle
Add to config/bundles.php:
Conduction\CommonGroundBundle\CommonGroundBundle::class => ['all' => true],
Copy conduction_common_ground.yaml from vendor/conduction/commongroundbundle/resources/config to config/packages/.
First Use Case: API Standard Compliance
Configure your API Platform resources to adhere to the VNG API Standard by extending Conduction\CommonGroundBundle\ApiPlatform\AbstractResource:
use Conduction\CommonGroundBundle\ApiPlatform\AbstractResource;
class MyResource extends AbstractResource
{
// Implement required methods like getCollectionOperations(), getItemOperations(), etc.
}
Verify Configuration
Check config/packages/conduction_common_ground.yaml for:
api_version: Your API version (e.g., v1).api_title: Your API title (e.g., My Dutch Government API).api_description: API description.api_contact: Contact details (email/URL).Leverage events for pre/post-processing:
// src/EventSubscriber/MyResourceSubscriber.php
use Conduction\CommonGroundBundle\Event\ResourceEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class MyResourceSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
ResourceEvent::RESOURCE_CREATE => 'onPreCreate',
ResourceEvent::RESOURCE_SAVED => 'onPostSave',
];
}
public function onPreCreate(ResourceEvent $event)
{
$resource = $event->getResource();
// Add BSN validation or KVK lookup logic here
}
public function onPostSave(ResourceEvent $event)
{
// Log or notify after save
}
}
Use the built-in validator:
# config/packages/conduction_common_ground.yaml
services:
conduction.common_ground.bsn_validator:
arguments:
$strict: true # Enforce strict validation
Validate in a controller or form:
use Conduction\CommonGroundBundle\Validator\Constraints\ValidBSN;
class MyController
{
public function create(Request $request, BSNValidator $validator)
{
$data = $request->request->all();
$validator->validateBSN($data['bsn']); // Throws \InvalidArgumentException on failure
}
}
Integrate KVK checks via the KvkClient:
use Conduction\CommonGroundBundle\Service\KvkClient;
class MyService
{
public function __construct(private KvkClient $kvkClient) {}
public function checkCompany(string $kvkNumber): array
{
return $this->kvkClient->fetch($kvkNumber);
}
}
Auto-generate OpenAPI metadata by extending AbstractResource:
use Conduction\CommonGroundBundle\ApiPlatform\AbstractResource;
use Nelmio\ApiDocBundle\Annotation\Model;
#[Model]
class MyResource extends AbstractResource
{
public function getCollectionOperations()
{
return [
'get' => [
'method' => 'GET',
'path' => '/my-resources',
'controller' => self::class,
'read' => false,
'openapi' => [
'summary' => 'List my resources',
'description' => 'Returns a paginated list of resources.',
],
],
];
}
}
Align with VNG standards using Conduction\CommonGroundBundle\ApiPlatform\Pagination\PaginationContextBuilder:
# config/packages/api_platform.yaml
api_platform:
pagination:
enabled: true
client_items_per_page: true
context_builder: conduction.common_ground.pagination_context_builder
Event Dispatching Order
RESOURCE_SAVE fire before validation. Use RESOURCE_CREATED/RESOURCE_UPDATED for post-validation logic.// ❌ Runs before validation (may fail silently)
ResourceEvent::RESOURCE_SAVE => 'onSave'
// ✅ Runs after validation (safe for side effects)
ResourceEvent::RESOURCE_CREATED => 'onCreated'
BSN Validation Strictness
$strict: true in conduction_common_ground.yaml to reject invalid BSNs early (e.g., 000000000 or 123456789).$this->validator->expects($this->once())->method('validateBSN')->with('123456789');
KVK API Rate Limits
use Symfony\Contracts\Cache\CacheInterface;
class KvkClient
{
public function __construct(private CacheInterface $cache) {}
public function fetch(string $kvkNumber)
{
return $this->cache->get($kvkNumber, function() use ($kvkNumber) {
return $this->httpClient->request('GET', "https://api.kvk.nl/v1/undertakens/$kvkNumber");
}, 3600); // Cache for 1 hour
}
}
API Versioning
/api/v1/resources). Ensure your api_version in conduction_common_ground.yaml matches your route prefixes.Symfony Flex Compatibility
bundles.php and parameters to avoid:
[Symfony\Component\Config\Exception\FileLoaderLoadException]
Cannot import resource "..." because it does not exist.
Event Debugging Dump dispatched events in a subscriber:
public function onResourceEvent(ResourceEvent $event)
{
\dump($event->getResource(), $event->getName());
}
Validator Errors
Catch InvalidArgumentException for BSN/KVK failures:
try {
$validator->validateBSN($bsn);
} catch (\InvalidArgumentException $e) {
throw new \RuntimeException('Invalid BSN: ' . $e->getMessage());
}
OpenAPI Schema Issues
If metadata isn’t reflected in /api/doc, clear the cache:
php bin/console cache:clear
Custom Validators
Extend Conduction\CommonGroundBundle\Validator\Constraints\ValidBSN:
namespace App\Validator;
use Conduction\CommonGroundBundle\Validator\Constraints\ValidBSN as BaseValidBSN;
use Symfony\Component\Validator\Constraint;
#[Attribute]
class ValidBSN extends BaseValidBSN
{
public string $message = 'This BSN is invalid or inactive.';
}
KVK Client Extensions Override the KVK client to add retries or logging:
use Conduction\CommonGroundBundle\Service\KvkClient as BaseKvkClient;
class CustomKvkClient extends BaseKvkClient
{
protected function fetchFromApi(string $kvkNumber): array
{
// Add retry logic or custom headers
return parent::fetchFromApi($kvkNumber);
}
}
Register as a service:
services:
conduction.common_ground.kvk_client:
class: App\Service\CustomKvkClient
arguments: [!tagged 'http_client']
Event Priorities
Control event subscriber priority (default: 0):
public static function getSubscribedEvents()
{
return [
ResourceEvent::RESOURCE_CREATE => ['onPreCreate', 20], // Higher priority
];
}
How can I help you explore Laravel packages today?