truncate('semaphore')->execute(); } catch (\Exception $e) { // Ignore if table doesn't exist yet. } $entity_type_manager = \Drupal::entityTypeManager(); $storage_node_type = $entity_type_manager->getStorage('node_type'); $storage_role = $entity_type_manager->getStorage('user_role'); $storage_field_storage = $entity_type_manager->getStorage('field_storage_config'); $storage_field_config = $entity_type_manager->getStorage('field_config'); // --- Content types (idempotent) --- if (!$storage_node_type->load('appointment')) { NodeType::create([ 'type' => 'appointment', 'name' => 'Appointment', 'description' => 'A booking between a Patient and a Provider at a particular time.', 'new_revision' => FALSE, 'display_submitted' => FALSE, ])->save(); } if (!$storage_node_type->load('provider_availability')) { NodeType::create([ 'type' => 'provider_availability', 'name' => 'Provider Availability', 'description' => 'A window of time during which a Provider is available for appointments.', 'new_revision' => FALSE, 'display_submitted' => FALSE, ])->save(); } // --- Role (idempotent) --- if (!$storage_role->load('provider')) { Role::create(['id' => 'provider', 'label' => 'Provider'])->save(); } // --- Field storages (idempotent) --- $field_storages = [ 'field_appointment_date' => [ 'entity_type' => 'node', 'type' => 'datetime', 'settings' => ['datetime_type' => 'datetime'], ], 'field_duration_minutes' => [ 'entity_type' => 'node', 'type' => 'integer', ], 'field_service_type' => [ 'entity_type' => 'node', 'type' => 'list_string', 'settings' => [ 'allowed_values' => [ 'diagnostic' => 'Diagnostic', 'sports_rehab' => 'Sports Rehab', 'pre_post_surgical_rehab' => 'Pre/Post-Surgical Rehab', 'neurological_pt' => 'Neurological PT', ], ], ], 'field_provider' => [ 'entity_type' => 'node', 'type' => 'entity_reference', 'settings' => ['target_type' => 'user'], ], 'field_start_datetime' => [ 'entity_type' => 'node', 'type' => 'datetime', 'settings' => ['datetime_type' => 'datetime'], ], 'field_end_datetime' => [ 'entity_type' => 'node', 'type' => 'datetime', 'settings' => ['datetime_type' => 'datetime'], ], ]; foreach ($field_storages as $field_name => $definition) { if (!$storage_field_storage->load("node.$field_name")) { FieldStorageConfig::create([ 'field_name' => $field_name, ] + $definition)->save(); } } // Clear field definition cache so FieldConfig creation can see the storages. \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); // --- Field configs (idempotent) --- $field_configs = [ // Appointment bundle 'node.appointment.field_appointment_date' => [ 'field_name' => 'field_appointment_date', 'entity_type' => 'node', 'bundle' => 'appointment', 'label' => 'Appointment Date', 'required' => TRUE, ], 'node.appointment.field_duration_minutes' => [ 'field_name' => 'field_duration_minutes', 'entity_type' => 'node', 'bundle' => 'appointment', 'label' => 'Duration (Minutes)', 'required' => TRUE, ], 'node.appointment.field_service_type' => [ 'field_name' => 'field_service_type', 'entity_type' => 'node', 'bundle' => 'appointment', 'label' => 'Service Type', 'required' => TRUE, ], 'node.appointment.field_provider' => [ 'field_name' => 'field_provider', 'entity_type' => 'node', 'bundle' => 'appointment', 'label' => 'Provider', 'required' => TRUE, 'settings' => [ 'handler' => 'default:user', 'handler_settings' => [ 'filter' => ['type' => '_role', 'role' => ['provider' => 'provider']], ], ], ], // Provider availability bundle 'node.provider_availability.field_provider' => [ 'field_name' => 'field_provider', 'entity_type' => 'node', 'bundle' => 'provider_availability', 'label' => 'Provider', 'required' => TRUE, 'settings' => [ 'handler' => 'default:user', 'handler_settings' => [ 'filter' => ['type' => '_role', 'role' => ['provider' => 'provider']], ], ], ], 'node.provider_availability.field_start_datetime' => [ 'field_name' => 'field_start_datetime', 'entity_type' => 'node', 'bundle' => 'provider_availability', 'label' => 'Start', 'required' => TRUE, ], 'node.provider_availability.field_end_datetime' => [ 'field_name' => 'field_end_datetime', 'entity_type' => 'node', 'bundle' => 'provider_availability', 'label' => 'End', 'required' => TRUE, ], ]; foreach ($field_configs as $field_id => $definition) { if (!$storage_field_config->load($field_id)) { FieldConfig::create($definition)->save(); } } // Navigation and basic site settings (these are intentionally destructive on menu). try { _riverside_pt_build_navigation(); } catch (\Exception $e) { \Drupal::logger('riverside_pt')->error('Navigation setup failed: @msg', ['@msg' => $e->getMessage()]); } \Drupal::configFactory()->getEditable('system.site') ->set('name', 'Riverside Therapeutics') ->set('page.front', '/home') ->save(); } function _riverside_pt_build_navigation(): void { $em = \Drupal::entityTypeManager(); foreach ($em->getStorage('menu_link_content')->loadByProperties(['menu_name' => 'main']) as $link) { $link->delete(); } // Clean up legacy nodes for pages we now serve via custom controllers/routes foreach (['About', 'Contact'] as $title) { $nodes = $em->getStorage('node')->loadByProperties(['title' => $title, 'type' => 'page']); foreach ($nodes as $node) { $node->delete(); } $aliases = $em->getStorage('path_alias')->loadByProperties(['alias' => '/' . strtolower($title)]); foreach ($aliases as $alias) { $alias->delete(); } } $defs = [ ['title' => 'Home', 'uri' => 'route:', 'weight' => 0, 'class' => NULL], ['title' => 'Services', 'uri' => 'internal:/services', 'weight' => 1, 'class' => NULL], ['title' => 'About', 'uri' => 'internal:/about', 'weight' => 2, 'class' => NULL], ['title' => 'FAQ', 'uri' => 'internal:/faq', 'weight' => 3, 'class' => NULL], ['title' => 'Contact', 'uri' => 'internal:/contact', 'weight' => 4, 'class' => 'nav-cta nav-cta--primary'], ['title' => 'Book An Appointment', 'uri' => 'internal:/book-appointment', 'weight' => 5, 'class' => 'nav-cta nav-cta--primary'], ]; foreach ($defs as $def) { $options = $def['class'] ? ['attributes' => ['class' => explode(' ', $def['class'])]] : []; MenuLinkContent::create([ 'title' => $def['title'], 'link' => ['uri' => $def['uri'], 'options' => $options], 'menu_name' => 'main', 'weight' => $def['weight'], 'enabled' => TRUE, ])->save(); } }