## Getting Started
### Minimal Steps to First Use
1. **Installation**:
- Add the bundle via Composer (automatically included in `php-fhir-tools` setup) or manually register in `config/bundles.php`:
```php
\Ardenexal\FHIRTools\Bundle\FHIRBundle\src\FHIRBundle::class => ['all' => true],
```
- Configure basic settings in `config/packages/fhir.yaml`:
```yaml
fhir:
default_version: R4B
output_directory: '%kernel.project_dir%/var/fhir'
cache_directory: '%kernel.cache_dir%/fhir'
```
2. **First Use Case**:
- Generate FHIR models for a package (e.g., HL7 FHIR R4 Core):
```bash
php bin/console fhir:generate --package=hl7.fhir.r4.core
```
- Verify models are generated in `var/fhir` (or your configured `output_directory`).
3. **Quick Serialization Test**:
- Inject `FHIRSerializationService` into a controller/service and serialize a FHIR object:
```php
use Ardenexal\FHIRTools\Component\Serialization\FHIRSerializationService;
public function __construct(private FHIRSerializationService $serializer) {}
public function testSerialization(): string {
$patient = new \Ardenexal\FHIRTools\Component\FHIR\R4\Patient(); // Auto-generated
return $this->serializer->serializeToJson($patient);
}
```
---
## Implementation Patterns
### Core Workflows
1. **Model Generation Pipeline**:
- **On-Demand Generation**: Use `fhir:generate` command for one-time or ad-hoc model generation (e.g., for new FHIR versions).
- **CI/CD Integration**: Cache generated models in version control (e.g., `var/fhir`) and regenerate only when `composer.lock` or FHIR version changes.
- **Offline Mode**: Use `--offline` flag to avoid network calls during generation (requires pre-downloaded packages).
2. **Serialization/Deserialization**:
- **Automatic Type Handling**: The bundle generates PHP classes with typed properties (e.g., `Patient::setName(\Ardenexal\FHIRTools\Component\FHIR\R4\HumanName $name)`).
- **Lazy Loading**: Use `FHIRSerializationService` for runtime serialization/deserialization without manual reflection:
```php
$json = $this->serializer->serializeToJson($resource);
$resource = $this->serializer->deserializeFromJson($json, \Ardenexal\FHIRTools\Component\FHIR\R4\Patient::class);
```
- **Partial Updates**: Leverage FHIR’s patch semantics for incremental updates:
```php
$this->serializer->patch($patient, $patchJson);
```
3. **FHIRPath Queries**:
- **Runtime Evaluation**: Use `FHIRPathService` for dynamic queries (e.g., filtering patients by name):
```php
$results = $this->pathService->evaluate('Patient.where(name.given.contains("Smith"))', $patients);
```
- **Caching**: Enable path caching in `fhir.yaml` to optimize repeated queries:
```yaml
fhir:
path:
cache_size: 1000
```
- **Validation**: Validate FHIRPath expressions before runtime:
```bash
php bin/console fhir:path:validate "Patient.where(active = true)"
```
4. **Integration with Symfony Ecosystem**:
- **Cache Warmers**: Pre-load metadata cache during `cache:warmup` for performance:
```yaml
fhir:
serialization:
metadata_cache_pool: cache.fhir_metadata
enable_cache_warmer: true
```
- **Dependency Injection**: Prefer constructor injection for services (e.g., `FHIRSerializationService`) to ensure immutability and testability.
- **API Platform**: Combine with [API Platform](https://api-platform.com/) for auto-generated FHIR APIs:
```yaml
# config/packages/api_platform.yaml
api_platform:
formats:
jsonld:
mime_types: ['application/fhir+json']
```
5. **Testing**:
- **Unit Tests**: Use `Eris` (included in `require-dev`) to generate mock FHIR resources:
```php
use Giorgiosironi\Eris\Factory;
$factory = new Factory();
$patient = $factory->patient(['name' => ['given' => ['John'], 'family' => ['Doe']]]);
```
- **FHIRPath Tests**: Test queries with known inputs/outputs:
```php
$this->assertEquals(['Smith'], $this->pathService->evaluate('name.family', $patient)->toArray());
```
---
## Gotchas and Tips
### Pitfalls
1. **Model Generation Overhead**:
- **Issue**: Generating models for large packages (e.g., `hl7.fhir.r4.core`) can take minutes and produce thousands of classes.
- **Fix**: Generate only required packages or use `--offline` with pre-downloaded packages. Cache generated models in version control to avoid regenerating on every deploy.
2. **Cache Invalidation**:
- **Issue**: Changing `default_version` or package dependencies may require clearing the metadata cache:
```bash
php bin/console cache:clear
```
- **Tip**: Use a dedicated cache pool (`cache.fhir_metadata`) to avoid conflicts with other Symfony caches.
3. **FHIRPath Performance**:
- **Issue**: Complex FHIRPath queries (e.g., nested `where` clauses) can be slow without caching.
- **Fix**: Enable path caching (`cache_size > 0`) and monitor cache hits/misses with `-v` flag:
```bash
php bin/console fhir:path:evaluate "..." -v
```
4. **Serialization Edge Cases**:
- **Issue**: Custom FHIR extensions or non-standard resources may not serialize/deserialize correctly.
- **Fix**: Extend generated classes or use `FHIRSerializationService::setCustomSerializer()` for custom logic:
```php
$serializer->setCustomSerializer(MyCustomResource::class, new MyCustomSerializer());
```
5. **PHP Version Compatibility**:
- **Issue**: The bundle requires PHP 8.3+, which may conflict with legacy projects.
- **Tip**: Use a separate Docker container or VM for FHIR-related development if upgrading PHP is not an option.
### Debugging Tips
1. **Verbose Logging**:
- Enable debug mode (`APP_ENV=dev`) and use `-vvv` with console commands to inspect generation/serialization steps:
```bash
php bin/console fhir:generate --package=hl7.fhir.r4.core -vvv
```
2. **Metadata Cache Inspection**:
- Check cached metadata in `var/cache/dev/fhir_metadata` (or your configured cache directory). Clear it manually if models behave unexpectedly:
```bash
rm -rf var/cache/dev/fhir_metadata/*
```
3. **FHIRPath Debugging**:
- Use the `fhir:path:evaluate` command interactively to test queries:
```bash
php bin/console fhir:path:evaluate "Patient.where(active = true)" patient.json --pretty
```
- Validate syntax before runtime:
```bash
php bin/console fhir:path:validate "Patient.where(name.given.contains('X'))"
```
### Extension Points
1. **Custom Generators**:
- Extend `FHIRModelGenerator` or `FHIRValueSetGenerator` to add custom logic (e.g., post-processing generated classes):
```php
use Ardenexal\FHIRTools\Component\CodeGeneration\Generator\FHIRModelGenerator;
class CustomModelGenerator extends FHIRModelGenerator {
protected function postProcessClass(string $className, string $content): string {
// Add custom traits/interfaces
return str_replace('class Patient', 'class Patient implements FHIRResourceInterface', $content);
}
}
```
- Register the custom generator as a service:
```yaml
services:
fhir.model_generator:
class: App\CustomModelGenerator
```
2. **Custom Serializers**:
- Implement `Ardenexal\FHIRTools\Component\Serialization\Serializer\FHIRSerializerInterface` for custom resource types:
```php
class CustomSerializer implements FHIRSerializerInterface {
public function serializeToJson(object $resource): string { /* ... */ }
public function deserializeFromJson(string $json, string $class): object { /* ... */ }
}
```
- Bind it to specific classes:
```php
$serializer->setCustomSerializer(MyResource::class, new CustomSerializer());
```
3. **FHIRPath Extensions**:
- Add custom functions to `FHIRPathService` by extending the evaluator:
```php
use Ardenexal\FHIRTools\Component\FHIRPath\Evaluator\FHIRPathEvaluator;
class CustomEvaluator extends FHIRPathEvaluator {
public function register
How can I help you explore Laravel packages today?