Install the Bundle
Add to your composer.json:
composer require sitepark/atoolo-citygov-bundle
Enable in config/bundles.php:
return [
// ...
Sitepark\Atoolo\CityGovBundle\CityGovBundle::class => ['all' => true],
];
Configure Required Dependencies
Ensure atoolo/search-bundle and atoolo/resource-bundle are installed (handled automatically via composer require).
Verify Solr/Elasticsearch is configured in config/packages/atoolo_search.yaml:
atoolo_search:
clients:
default:
type: solr
host: 'http://solr:8983/solr'
core: 'citygov'
First Use Case: Indexing Municipal Content
Use the ContentCollector to index documents (e.g., ordinances, event listings):
use Sitepark\Atoolo\CityGovBundle\Collector\CityGovContentCollector;
$collector = $container->get(CityGovContentCollector::class);
$collector->collect($resource); // Pass a `ResourceInterface` (e.g., `Page`, `Document`)
Search for Persons (v1.5.0+) Leverage the search API for employee directories:
use Sitepark\Atoolo\CityGovBundle\Search\PersonSearch;
$search = new PersonSearch($searchClient);
$results = $search->search('John Doe', ['sort' => 'lastName']);
Extend DocumentEnricher to add metadata (e.g., sp_meta_string_leikanumber for Swiss/German compliance):
use Sitepark\Atoolo\CityGovBundle\Enricher\CityGovDocumentEnricher;
class CustomEnricher extends CityGovDocumentEnricher {
public function enrich($document, $resource) {
$document->set('sp_meta_string_leikanumber', $resource->getLeikaNumber());
return parent::enrich($document, $resource);
}
}
Register in services.yaml:
services:
Sitepark\Atoolo\CityGovBundle\Enricher\DocumentEnricher:
class: App\Enricher\CustomEnricher
Query online services via GraphQL:
query {
onlineServices {
id
title
description
link
}
}
Use the OnlineServiceFeature resolver:
use Sitepark\Atoolo\CityGovBundle\GraphQL\OnlineServiceFeature;
$feature = new OnlineServiceFeature($linkFactory);
$services = $feature->getOnlineServices();
Implement a search controller:
use Sitepark\Atoolo\CityGovBundle\Search\PersonSearch;
class PersonSearchController extends AbstractController {
public function search(Request $request, PersonSearch $personSearch) {
$query = $request->query->get('q');
$results = $personSearch->search($query, [
'sort' => $request->query->get('sort', 'lastName'),
'limit' => 10
]);
return $this->json($results);
}
}
Use ResourceLocation and ResourceLanguage for translations:
use Sitepark\Atoolo\CityGovBundle\Model\ResourceLocation;
$location = new ResourceLocation();
$location->setLanguage('de_CH'); // Swiss German
$location->setTitle('Verwaltung');
$resource->addLocation($location);
Extend the default schema to include CityGov-specific fields:
<!-- solrconfig.xml -->
<field name="sp_meta_string_leikanumber" type="string" indexed="true" stored="true"/>
<field name="startletter" type="string" indexed="true" stored="true" multiValued="false"/>
Trigger indexing on resource updates:
use Sitepark\Atoolo\CityGovBundle\Event\CityGovIndexEvent;
class IndexOnUpdateListener {
public function onResourceUpdate(CityGovIndexEvent $event) {
$collector = $event->getContainer()->get(CityGovContentCollector::class);
$collector->collect($event->getResource());
}
}
Register in services.yaml:
services:
App\EventListener\IndexOnUpdateListener:
tags:
- { name: kernel.event_listener, event: atoolo.resource.update, method: onResourceUpdate }
Use LinkFactory to generate compliant URLs (v1.5.0+):
use Sitepark\Atoolo\CityGovBundle\Service\LinkFactory;
$linkFactory = $container->get(LinkFactory::class);
$serviceUrl = $linkFactory->createOnlineServiceLink($serviceId);
Schema Mismatches
startletter) may not map correctly if the schema isn’t updated.php bin/console atoolo:search:reindex
Deprecated Namespaces
SiteKitSchema21 (deprecated in favor of SiteKitSchema2x).// Old (deprecated)
use Sitepark\Atoolo\CityGovBundle\Schema\SiteKitSchema21;
// New
use Sitepark\Atoolo\CityGovBundle\Schema\SiteKitSchema2x;
Missing Resource Argument
ContentCollector may fail if the ResourceInterface lacks required arguments (e.g., getLeikaNumber()).class CustomResource implements ResourceInterface {
public function getLeikaNumber() {
return $this->leikaNumber ?? null;
}
}
PHP 8.4+ Compatibility
null coalescing or @phpstan-ignore-line:
$value = $resource->getField() ?? null;
GraphQL Over-Fetching
OnlineServiceFeature may return excessive data.fragment OnlineServiceTeaser on OnlineService {
id
title
teaser
}
Check Indexing Logs
Enable debug mode for atoolo/search-bundle:
# config/packages/dev/atoolo_search.yaml
atoolo_search:
debug: true
Validate Solr Queries
Use the Solr admin UI (http://solr:8983/solr/#/citygov/query) to test queries manually.
Person Search Sorting
If sort criteria (e.g., lastName) fail, verify the field exists in Solr:
curl "http://solr:8983/solr/citygov/schema/fields?show=lastName"
Link Factory Errors Debug URL generation:
$linkFactory = $container->get(LinkFactory::class);
try {
$url = $linkFactory->createOnlineServiceLink($serviceId);
} catch (\Exception $e) {
// Log or rethrow with context
throw new \RuntimeException('Invalid service ID: ' . $serviceId, 0, $e);
}
Custom Document Enrichment
Override CityGovDocumentEnricher to add domain-specific fields:
class CustomEnricher extends CityGovDocumentEnricher {
public function enrich($document, $resource) {
$document->set('custom_field', $this->getCustomData($resource));
return parent::enrich($document, $resource);
}
}
**
How can I help you explore Laravel packages today?