Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,256 @@
# External Entity Mapping für Search System
**Suchfunktionalität ohne Entity-Änderungen** - Komplette Integration über externe Konfiguration.
## 1. Basic Mapping Configuration
```php
// In einem Service Provider oder Initializer
final class SearchMappingProvider
{
public static function registerMappings(SearchableMappingRegistry $registry): void
{
// User Entity Mapping
$userMapping = SearchableMapping::for(User::class)
->entityType('users')
->idField('id')
->field('name', 'name')
->field('email', 'email')
->field('bio', 'biography')
->field('created', 'createdAt', fn($date) => $date->format('Y-m-d'))
->boost('name', 2.0)
->boost('email', 1.5)
->autoIndex(true)
->build();
$registry->register($userMapping);
// Product Entity Mapping
$productMapping = SearchableMapping::for(Product::class)
->entityType('products')
->field('title', 'name')
->field('description', 'description')
->field('category', 'category.name') // Nested field access
->field('price', 'price', fn($price) => $price->getCents() / 100) // Transform Money object
->field('tags', 'getTags', fn($tags) => implode(' ', $tags)) // Method call + transform
->boost('title', 3.0)
->boost('description', 1.0)
->build();
$registry->register($productMapping);
}
}
```
## 2. Configuration-Based Mapping
```php
// config/search_mappings.php
return [
User::class => [
'entity_type' => 'users',
'id_field' => 'id',
'auto_index' => true,
'fields' => [
'name' => 'name',
'email' => 'email',
'bio' => 'biography',
'created' => [
'field' => 'createdAt',
'transformer' => fn($date) => $date->format('Y-m-d')
]
],
'boosts' => [
'name' => 2.0,
'email' => 1.5
]
],
Product::class => [
'entity_type' => 'products',
'fields' => [
'title' => 'name',
'description' => 'description',
'category' => 'category.name', // Nested access
'price' => [
'field' => 'price',
'transformer' => fn($price) => $price->toFloat()
]
],
'boosts' => [
'title' => 3.0
]
]
];
// Load configuration
$config = require 'config/search_mappings.php';
$registry->registerFromConfig($config);
```
## 3. Repository Integration (No Entity Changes)
```php
final class UserRepository
{
public function __construct(
private EntityManager $entityManager,
private SearchIndexingService $searchIndexing
) {
}
public function create(array $userData): User
{
$user = new User($userData);
$this->entityManager->persist($user);
$this->entityManager->flush();
// Auto-index after creation (if enabled)
$this->searchIndexing->indexEntity($user);
return $user;
}
public function update(User $user, array $changes): void
{
// Apply changes to user
foreach ($changes as $field => $value) {
$user->{"set" . ucfirst($field)}($value);
}
$this->entityManager->flush();
// Update search index
$this->searchIndexing->updateEntity($user);
}
public function delete(User $user): void
{
// Remove from search first
$this->searchIndexing->removeEntity($user);
$this->entityManager->remove($user);
$this->entityManager->flush();
}
public function reindexAll(): BulkIndexResult
{
return $this->searchIndexing->reindexEntityType('users', function() {
return $this->entityManager->findAll(User::class);
});
}
}
```
## 4. Service Usage Without Entity Knowledge
```php
final class ProductSearchService
{
public function __construct(
private SearchService $searchService,
private SearchableMappingRegistry $mappingRegistry
) {
}
public function makeProductSearchable(Product $product): bool
{
// Check if product is configured as searchable
if (!$this->mappingRegistry->isSearchable($product)) {
return false;
}
// Create adapter automatically
$adapter = $this->mappingRegistry->createAdapter($product);
if (!$adapter) {
return false;
}
// Index using the mapped fields
return $this->searchService->index(
$adapter->getEntityType(),
$adapter->getId(),
$adapter->toSearchDocument()
);
}
public function searchProducts(string $query): SearchResult
{
return $this->searchService
->for('products')
->query($query)
->boost('title', 3.0) // Use configured boosts
->limit(20)
->search();
}
}
```
## 5. Event-Driven Auto-Indexing
```php
// Events werden automatisch von Entity Operations gefeuert
$eventDispatcher->dispatch(new EntityCreatedEvent($user)); // Auto-indexes
$eventDispatcher->dispatch(new EntityUpdatedEvent($product)); // Auto-updates
$eventDispatcher->dispatch(new EntityDeletedEvent($order)); // Auto-removes
```
## 6. Advanced Field Transformations
```php
$mapping = SearchableMapping::for(Article::class)
->field('title', 'title')
->field('content', 'body')
->field('author', 'user.name') // Nested object access
->field('tags', 'tags', fn($tags) => implode(' ', array_column($tags, 'name')))
->field('published_date', 'publishedAt', fn($date) => $date?->format('Y-m-d'))
->field('word_count', 'body', fn($content) => str_word_count(strip_tags($content)))
->field('reading_time', 'body', fn($content) => ceil(str_word_count($content) / 200))
->build();
```
## 7. Conditional Indexing
```php
final class ConditionalSearchListener
{
#[EventListener(event: 'entity.updated')]
public function onEntityUpdated(EntityUpdatedEvent $event): void
{
$entity = $event->getEntity();
// Only index published articles
if ($entity instanceof Article && $entity->isPublished()) {
$this->indexingService->updateEntity($entity);
} elseif ($entity instanceof Article && !$entity->isPublished()) {
// Remove from index if unpublished
$this->indexingService->removeEntity($entity);
}
}
}
```
## Vorteile dieses Ansatzes:
**Keine Entity-Änderungen erforderlich**
**Zentrale Konfiguration** aller Mappings
**Flexible Field-Transformationen**
**Automatische Indexierung** über Events
**Repository-Integration** ohne Abhängigkeiten
**Conditional Indexing** möglich
**Backward Compatible** mit bestehenden Entities
## Integration:
```php
// Container Registration
$container->singleton(SearchableMappingRegistry::class);
$container->singleton(SearchIndexingService::class);
// Register mappings at startup
$registry = $container->get(SearchableMappingRegistry::class);
SearchMappingProvider::registerMappings($registry);
```
Dieser Ansatz ermöglicht **vollständige Suchfunktionalität ohne jegliche Änderungen an bestehenden Entity-Klassen**.