customer-riverside/web/modules/custom/riverside_pt/riverside_pt.install

240 lines
8 KiB
Text
Raw Normal View History

2026-05-01 04:24:10 -08:00
<?php
use Drupal\node\Entity\Node;
2026-05-01 04:24:10 -08:00
use Drupal\node\Entity\NodeType;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\path_alias\Entity\PathAlias;
2026-05-12 16:28:47 -08:00
use Drupal\user\Entity\Role;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
2026-05-01 04:24:10 -08:00
/**
* Implements hook_install().
*
* Delegates to the rebuild function so that normal module installs and
* container rebuilds both produce the same result.
*/
2026-05-01 04:24:10 -08:00
function riverside_pt_install() {
_riverside_pt_rebuild();
}
/**
* Rebuilds the Riverside PT site structure from code.
*
* This function is idempotent and can be safely called multiple times.
* It is the single source of truth for content types, fields, roles,
* and navigation used by the Docker entrypoint.
*/
function _riverside_pt_rebuild(): void {
$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',
],
2026-05-14 19:39:30 -08:00
],
],
'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.
2026-05-14 19:39:30 -08:00
\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']],
],
2026-05-14 19:39:30 -08:00
],
],
// 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']],
],
2026-05-14 19:39:30 -08:00
],
],
'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();
}
}
2026-05-14 19:39:30 -08:00
// Navigation and basic site settings (these are intentionally destructive on menu).
2026-05-14 19:39:30 -08:00
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')
2026-06-01 00:47:26 -08:00
->set('name', 'Riverside Therapeutics')
2026-05-14 19:39:30 -08:00
->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();
}
foreach (['Services', 'About', 'FAQ', 'Contact'] as $title) {
2026-05-14 19:39:30 -08:00
if ($em->getStorage('node')->loadByProperties(['title' => $title, 'type' => 'page'])) {
continue;
}
$node = Node::create(['type' => 'page', 'title' => $title, 'status' => 1]);
$node->save();
PathAlias::create([
'path' => '/node/' . $node->id(),
'alias' => '/' . strtolower($title),
'langcode' => 'en',
])->save();
}
$defs = [
['title' => 'Home', 'uri' => 'route:<front>', '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],
2026-05-16 08:52:21 -08:00
['title' => 'Contact', 'uri' => 'internal:/contact', 'weight' => 4, 'class' => 'nav-cta nav-cta--primary'],
['title' => 'Book An Appointment', 'uri' => 'internal:/home', '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();
}
2026-05-12 16:28:47 -08:00
}