120 lines
3.9 KiB
PHP
120 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\ServiceAgendaItem;
|
|
|
|
class AgendaMatcherService
|
|
{
|
|
/**
|
|
* Check if an item title matches a glob-style pattern.
|
|
* Supports * wildcard (matches any characters).
|
|
* Case-insensitive.
|
|
*/
|
|
public function matches(string $itemTitle, string $pattern): bool
|
|
{
|
|
$normalizedTitle = strtolower(trim($itemTitle));
|
|
$normalizedPattern = strtolower(trim($pattern));
|
|
|
|
return fnmatch($normalizedPattern, $normalizedTitle, FNM_CASEFOLD);
|
|
}
|
|
|
|
/**
|
|
* Check if an item title matches any of the patterns.
|
|
*
|
|
* @param array $patterns Array of glob-style patterns
|
|
*/
|
|
public function matchesAny(string $itemTitle, array $patterns): bool
|
|
{
|
|
foreach ($patterns as $pattern) {
|
|
if ($this->matches($itemTitle, $pattern)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Find the first ServiceAgendaItem whose title matches any of the patterns.
|
|
*
|
|
* @param array $agendaItems Array of ServiceAgendaItem
|
|
* @param string $patterns Comma-separated patterns (e.g. "Information*,Hinweis*")
|
|
*/
|
|
public function findFirstMatch(array $agendaItems, string $patterns): ?ServiceAgendaItem
|
|
{
|
|
$patternList = array_map(fn ($p) => trim($p), explode(',', $patterns));
|
|
$patternList = array_filter($patternList, fn ($p) => $p !== '');
|
|
|
|
foreach ($agendaItems as $item) {
|
|
if ($this->matchesAny($item->title, $patternList)) {
|
|
return $item;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Filter array of ServiceAgendaItems to only those between start and end boundary items.
|
|
* Boundary items are identified by matching start/end patterns (comma-separated strings).
|
|
* The boundary items themselves are EXCLUDED from the result.
|
|
*
|
|
* If startPattern is empty/null: start from the first item
|
|
* If endPattern is empty/null: go to the last item
|
|
*
|
|
* @param array $items Array of ServiceAgendaItem
|
|
* @param string|null $startPattern Comma-separated start patterns or null
|
|
* @param string|null $endPattern Comma-separated end patterns or null
|
|
* @return array Filtered items (boundaries excluded)
|
|
*/
|
|
public function filterBetween(array $items, ?string $startPattern, ?string $endPattern): array
|
|
{
|
|
if (empty($items)) {
|
|
return [];
|
|
}
|
|
|
|
$startIdx = 0;
|
|
$endIdx = count($items) - 1;
|
|
|
|
// Find start boundary index
|
|
if ($startPattern !== null && $startPattern !== '') {
|
|
$startItem = $this->findFirstMatch($items, $startPattern);
|
|
if ($startItem === null) {
|
|
// No matching start pattern — return empty
|
|
return [];
|
|
}
|
|
// Find the index of the start item
|
|
foreach ($items as $index => $item) {
|
|
if ($item->id === $startItem->id) {
|
|
$startIdx = $index + 1; // Exclude the boundary item
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find end boundary index
|
|
if ($endPattern !== null && $endPattern !== '') {
|
|
$endItem = $this->findFirstMatch($items, $endPattern);
|
|
if ($endItem === null) {
|
|
// No matching end pattern — return empty
|
|
return [];
|
|
}
|
|
// Find the index of the end item (search from end for clarity)
|
|
foreach (array_reverse($items, true) as $index => $item) {
|
|
if ($item->id === $endItem->id) {
|
|
$endIdx = $index - 1; // Exclude the boundary item
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return slice
|
|
if ($startIdx > $endIdx) {
|
|
return [];
|
|
}
|
|
|
|
return array_slice($items, $startIdx, $endIdx - $startIdx + 1);
|
|
}
|
|
}
|