<?php declare(strict_types=1);
namespace App\StartPlatz\Bundle\MentorsBundle\Controller;
use App\StartPlatz\Bundle\MentorsBundle\Service\MentorService;
use App\StartPlatz\Bundle\MentorsBundle\Service\WordPressMenuService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* PublicController
*
* Handles public-facing mentor and expert pages
*/
class PublicController extends AbstractController
{
private MentorService $mentorService;
private WordPressMenuService $menuService;
private EntityManagerInterface $entityManager;
public function __construct(
MentorService $mentorService,
WordPressMenuService $menuService,
EntityManagerInterface $entityManager
) {
$this->mentorService = $mentorService;
$this->menuService = $menuService;
$this->entityManager = $entityManager;
}
/**
* STARTPLATZ Mentors page
*/
#[Route('/mentors/startplatz', name: 'mentors_startplatz')]
#[Route('/en/mentors/startplatz', name: 'mentors_startplatz_en')]
public function startplatzMentorsAction(Request $request): Response
{
$locale = $request->getLocale();
$isEnglish = str_contains($request->getPathInfo(), '/en/');
// Get all mentors (no pagination for section-based display)
$allMentors = $this->mentorService->getMentorsByCategory(
MentorService::CATEGORY_STARTPLATZ,
[]
);
// Define categories in the order they should appear (matching original page)
$categoryOrder = [
'apps-it' => 'Apps und IT',
'marketing' => 'Marketing',
'finanzierung' => 'Finanzierung',
'gruendung-fuehrung' => 'Gründung und Führung',
'recht' => 'Recht',
'design' => 'Design',
'wirtschaftspruefung-steuerberatung' => 'Wirtschaftsprüfung und Steuerberatung',
'versicherung' => 'Versicherung'
];
// Group mentors by category
$mentorsByCategory = [];
// Initialize all categories even if empty
foreach ($categoryOrder as $categoryKey => $categoryLabel) {
$mentorsByCategory[$categoryKey] = [
'label' => $categoryLabel,
'mentors' => []
];
}
// Process each mentor
foreach ($allMentors as $mentorProfile) {
$member = $mentorProfile->getMember();
$metadata = $mentorProfile->getMetadata() ?? [];
// Get mentor's categories from metadata ONLY
// If no categories defined, assign to default category
$mentorCategories = $metadata['categories'] ?? ['gruendung-fuehrung'];
if (!is_array($mentorCategories)) {
$mentorCategories = [$mentorCategories];
}
// Prepare mentor data
$mentorData = [
'id' => $member->getId(),
'name' => trim($member->getFirstName() . ' ' . $member->getLastName()),
'firstName' => $member->getFirstName(),
'lastName' => $member->getLastName(),
'email' => $member->getEmail(),
'title' => $member->getJobTitle() ?? '',
'academicTitle' => $member->getTitle() ?? '',
'company' => $mentorProfile->getFirma() ?? $member->getCompany() ?? '',
'description' => $mentorProfile->getProfileDe() ?? $member->getDescription() ?? '',
'image' => $member->getImageLink() ?? 'https://res.cloudinary.com/startplatz/image/upload/v1637360496/Wordpress/Sprechstunden/Sprechstunden_Platzhalter.jpg',
'skills' => [],
'slug' => strtolower(str_replace(' ', '-', trim($member->getFirstName() . '-' . $member->getLastName())))
];
// Add to appropriate categories (or to first category if none specified)
if (empty($mentorCategories)) {
// If no category specified, add to first category
$firstKey = array_key_first($categoryOrder);
$mentorsByCategory[$firstKey]['mentors'][] = $mentorData;
} else {
// Add to all specified categories
foreach ($mentorCategories as $category) {
if (isset($mentorsByCategory[$category])) {
$mentorsByCategory[$category]['mentors'][] = $mentorData;
}
}
}
}
// Remove empty categories if needed (optional)
// $mentorsByCategory = array_filter($mentorsByCategory, function($category) {
// return !empty($category['mentors']);
// });
return $this->render('@StartPlatzMentors/Public/startplatz-mentors.html.twig', [
'mentorsByCategory' => $mentorsByCategory,
'isEnglish' => $isEnglish
]);
}
/**
* Categorize mentor by their skills
*/
private function categorizeMentorBySkills(array $skills): array
{
// Define category keywords (German and English)
$categoryKeywords = [
'tech-development' => [
'entwicklung', 'development', 'programming', 'software', 'code', 'coding',
'python', 'javascript', 'java', 'php', 'backend', 'frontend', 'fullstack',
'devops', 'cloud', 'aws', 'docker', 'kubernetes', 'api', 'database', 'sql',
'machine learning', 'ai', 'ki', 'artificial intelligence', 'deep learning',
'neural', 'tensorflow', 'pytorch', 'data science', 'algorithm', 'tech', 'it',
'mobile', 'app', 'web', 'security', 'blockchain', 'crypto', 'iot', 'embedded',
'github', 'gitlab', 'git', 'bitbucket', 'technologie', 'technology',
'react', 'angular', 'vue', 'node', 'typescript', 'swift', 'kotlin',
'android', 'ios', 'flutter', 'xamarin', 'unity', 'unreal',
'azure', 'gcp', 'serverless', 'microservices', 'ci/cd', 'jenkins',
'mysql', 'postgresql', 'mongodb', 'redis', 'elasticsearch', 'ingenieurwesen'
],
'business-strategy' => [
'strategy', 'strategie', 'business', 'geschäft', 'model', 'skalierung',
'scaling', 'growth', 'wachstum', 'management', 'führung', 'leadership',
'operations', 'prozesse', 'transformation', 'innovation', 'planung',
'roadmap', 'vision', 'mission', 'okr', 'kpi', 'agile', 'scrum', 'lean',
'startup', 'gründung', 'entrepreneurship', 'ceo', 'coo', 'executive',
'consulting', 'beratung', 'change', 'digital transformation'
],
'sales-marketing' => [
'sales', 'vertrieb', 'marketing', 'growth', 'hacking', 'b2b', 'b2c',
'seo', 'sea', 'sem', 'social media', 'content', 'brand', 'marke', 'pr',
'communication', 'kommunikation', 'crm', 'lead', 'conversion', 'funnel',
'customer', 'acquisition', 'retention', 'email', 'advertising', 'werbung',
'campaign', 'analytics', 'performance', 'influencer', 'storytelling',
'copywriting', 'demand', 'generation', 'outbound', 'inbound'
],
'finance-legal' => [
'finance', 'finanzen', 'funding', 'investment', 'investor', 'vc',
'venture', 'capital', 'förderung', 'grant', 'pitch', 'valuation',
'legal', 'recht', 'anwalt', 'lawyer', 'compliance', 'dsgvo', 'gdpr',
'vertrag', 'contract', 'ip', 'patent', 'steuer', 'tax', 'accounting',
'buchführung', 'controlling', 'budget', 'cashflow', 'revenue', 'profit',
'seed', 'series', 'exit', 'ipo', 'due diligence', 'term sheet'
],
'product-design' => [
'product', 'produkt', 'design', 'ux', 'ui', 'user experience', 'usability',
'customer', 'development', 'mvp', 'prototype', 'iteration', 'lean startup',
'feedback', 'research', 'testing', 'interface', 'interaction',
'creative', 'kreativ', 'brand', 'visual', 'figma', 'sketch', 'adobe',
'wireframe', 'mockup', 'persona', 'journey', 'map', 'sprint',
'discovery', 'validation', 'feature', 'roadmap', 'backlog'
]
];
if (empty($skills)) {
return ['business-strategy']; // Default category
}
// Convert skills to lowercase for matching
$skillsLower = array_map('strtolower', $skills);
$categoryScores = [];
foreach ($categoryKeywords as $category => $keywords) {
$score = 0;
$matches = [];
foreach ($skillsLower as $skill) {
// Remove hashtag if present
$skill = ltrim(trim($skill), '#');
// Split hyphenated words for better matching
$skillParts = preg_split('/[-\s]+/', $skill);
foreach ($keywords as $keyword) {
// Exact match: 3 points
if ($skill === $keyword) {
$score += 3;
$matches[] = $skill;
break; // Don't count same skill multiple times
}
// Check parts of hyphenated words
foreach ($skillParts as $part) {
if ($part === $keyword) {
$score += 2; // Give 2 points for exact part match
$matches[] = $skill;
break 2; // Break both loops
}
}
// Partial match: 1 point - but only for whole word matches
// Use word boundaries to avoid false positives like "it" in "exit"
if (preg_match('/\b' . preg_quote($keyword, '/') . '\b/i', $skill)) {
$score += 1;
$matches[] = $skill;
break;
}
}
}
// Only consider categories with score >= 2
if ($score >= 2) {
$categoryScores[$category] = [
'score' => $score,
'matches' => array_unique($matches)
];
}
}
// If no categories matched, return default
if (empty($categoryScores)) {
return ['business-strategy'];
}
// Sort by score and return top 3 categories
arsort($categoryScores);
return array_slice(array_keys($categoryScores), 0, 3);
}
/**
* AI Accelerator Mentors page
*/
#[Route('/accelerator/mentors', name: 'accelerator_mentors')]
#[Route('/en/accelerator/mentors', name: 'accelerator_mentors_en')]
public function acceleratorMentorsAction(Request $request): Response
{
$isEnglish = str_contains($request->getPathInfo(), '/en/');
// Get filters
$filters = [
'search' => $request->get('search'),
'expertise' => $request->get('expertise'),
'techStack' => $request->get('tech')
];
// Get mentors
$allMentors = $this->mentorService->getMentorsByCategory(
MentorService::CATEGORY_ACCELERATOR,
array_filter($filters)
);
// Prepare mentor data arrays
$mentorsData = [];
foreach ($allMentors as $mentorProfile) {
$member = $mentorProfile->getMember();
$metadata = $mentorProfile->getMetadata() ?? [];
// Prepare skills array from member
$skills = [];
$skillsString = $member->getSkills();
if (!empty($skillsString)) {
$skills = preg_split('/[\s,]+/', $skillsString);
$skills = array_filter($skills, function($s) { return !empty(trim($s)); });
}
// Categorize mentor based on skills
$categories = $this->categorizeMentorBySkills($skills);
// Prepare mentor data
$mentorData = [
'id' => $member->getId(),
'name' => trim($member->getFirstName() . ' ' . $member->getLastName()),
'firstName' => $member->getFirstName(),
'lastName' => $member->getLastName(),
'email' => $member->getEmail(),
'title' => $member->getJobTitle() ?? $metadata['title'] ?? '',
'academicTitle' => $member->getTitle() ?? '',
'company' => $member->getCompany() ?? $metadata['company'] ?? '',
'mentorFirma' => $mentorProfile->getFirma(),
'description' => $member->getDescription() ?? $metadata['description'] ?? '',
'profileImage' => $member->getImageLink() ?? $metadata['profileImage'] ?? null,
'image' => $member->getImageLink() ?? $metadata['profileImage'] ?? null,
'skills' => $skills,
'categories' => $categories,
'expertise' => $metadata['expertise'] ?? null,
'linkedin' => $metadata['linkedin'] ?? null,
'mentorProfile' => $mentorProfile->getProfileDe(),
'jobTitle' => $member->getJobTitle(),
'mentorMetadata' => $metadata,
'github' => $metadata['github'] ?? null,
'twitter' => $metadata['twitter'] ?? null
];
$mentorsData[] = $mentorData;
}
// Get menu from WordPress
$menu = $this->menuService->getAcceleratorMenu($isEnglish, $request->getPathInfo());
return $this->render('@StartPlatzMentors/Public/accelerator-mentors.html.twig', [
'mentors' => $mentorsData,
'searchQuery' => $filters['search'] ?? '',
'currentExpertise' => $filters['expertise'] ?? null,
'currentTechStack' => $filters['techStack'] ?? null,
'isEnglish' => $isEnglish,
'pageTitle' => $isEnglish ? 'AI Accelerator Mentors' : 'KI Accelerator Mentoren',
'pageDescription' => $isEnglish
? 'Expert mentors for AI startups'
: 'Experten-Mentoren für KI-Startups',
'menu' => $menu
]);
}
/**
* Experts in Residence page
*/
#[Route('/accelerator/experts-in-residence', name: 'experts_residence')]
#[Route('/en/accelerator/experts-in-residence', name: 'experts_residence_en')]
public function expertsResidenceAction(Request $request): Response
{
$isEnglish = str_contains($request->getPathInfo(), '/en/');
// Get filters
$filters = [
'search' => $request->get('search'),
'specialization' => $request->get('specialization'),
'limit' => 20,
'offset' => ($request->get('page', 1) - 1) * 20
];
// Get experts
$allExperts = $this->mentorService->getMentorsByCategory(
MentorService::CATEGORY_EXPERTS,
array_filter($filters)
);
// Prepare expert data arrays
$expertsData = [];
foreach ($allExperts as $mentorProfile) {
$member = $mentorProfile->getMember();
$metadata = $mentorProfile->getMetadata() ?? [];
// Prepare expert data
$expertData = [
'id' => $member->getId(),
'name' => trim($member->getFirstName() . ' ' . $member->getLastName()),
'firstName' => $member->getFirstName(),
'lastName' => $member->getLastName(),
'email' => $member->getEmail(),
'profileImage' => $member->getImageLink(),
'whoAmI' => $member->getDescription() ?: $mentorProfile->getProfileDe(),
'skills' => $member->getSkills(),
'linkedin' => $member->getLinkedin(),
'twitter' => $member->getTwitter(),
'expertMetadata' => $metadata,
'title' => $member->getJobTitle() ?? $metadata['title'] ?? '',
'academicTitle' => $member->getTitle() ?? '',
'company' => $member->getCompany() ?? $metadata['companyAffiliation'] ?? '',
'description' => $member->getDescription(),
'image' => $member->getImageLink(),
'specializations' => $metadata['expertiseAreas'] ?? $mentorProfile->getExpertiseAreas() ?? [],
'currentProject' => $metadata['currentProject'] ?? null,
'website' => $metadata['website'] ?? null
];
$expertsData[] = $expertData;
}
// Determine language for template
$language = $isEnglish ? 'en' : 'de';
// Get menu from WordPress
$menu = $this->menuService->getAcceleratorMenu($isEnglish, $request->getPathInfo());
return $this->render('@StartPlatzMentors/Public/experts-residence.html.twig', [
'experts' => $expertsData,
'searchQuery' => $filters['search'] ?? '',
'currentSpecialization' => $filters['specialization'] ?? null,
'isEnglish' => $isEnglish,
'language' => $language,
'pageTitle' => 'Expert-in-Residence Program',
'pageDescription' => $isEnglish
? 'Making local excellence in Artificial Intelligence visible and impactful'
: 'Lokale Exzellenz in der Künstlichen Intelligenz sichtbar und wirksam machen',
'menu' => $menu
]);
}
}