```
declare(strict_types=1);
namespace app\controllers;

class ContentController extends BaseController
{
	private bool $_debugMode = true;
	
	// Note: Every protected variable below CacheJsCssTrait is used by the trait
	use CacheJsCssTrait;
	
	protected bool $_cacheDebugMode = false;
	protected bool $_compressJsCss = false;
	protected bool $_cacheEnable = false;
	protected int $_cacheJsCssTime = 3600;// +1 hour
	protected string $_cacheKeyPrefix = 'contentController_jqury_';
	
	const DEFAULT_DURATION_SECONDS = 3600;// +1 hour
	
	/**
	* Creates a new model.
	* @return yii\web\Response|string
	*
	* If creation is successful, the browser will be redirected to 
	* the 'view' page.
	*
	* Note: Example code of debugging within Controller Action
	*/
	public function actionCreate(): Response|string
	{
		$model = new Content();
		if (!$model)
		{
			throw new YiiWebNotFoundHttpException(\Yii::t('app', 'Content not found'));
		}
		
		// Setup category/options data early
		$data = $this->getCategoryOptions(true);
		
		// For actionCreate only! Assign a default category_id if none is set (e.g. first in dropdown)
		if (!$model->category_id && !empty($data['categoryOptions']))
		{
			reset($data['categoryOptions']);
			$defaultCategoryId = key($data['categoryOptions']);
			$model->category_id = $defaultCategoryId;
		}
		
		$session = \Yii::$app->session;
		$request = \Yii::$app->request;
		
		if ($this->_debugMode)
		{
			LogHelper::debug('POST data (before load): ' . json_encode($request->post()), [
				'post data' => json_encode($request->post()),
				'method' => __METHOD__
			]);
		}
		
		if (($request->isPost) && ($model->load($request->post())))
		//if (($request->isPost) && ($model->load($request->post()) && $model->validate()))
		{
			if ($this->_debugMode)
			{
				LogHelper::debug('Model and POST data after loading:', [
					'model_attributes_json' => json_encode($model->attributes),
					'model_attributes_array' => $model->getAttributes(),
					'post_data' => $request->post(),
					'method' => __METHOD__
				]);
			}
			
			if ($model->validate())
			{
				if ($model->save())
				{
					if ($this->_debugMode)
					{
						LogHelper::debug('Saved content ID: ' . $model->id, [
							'model ID' => $model->id,
							//'price' => $model->price,// etc
							'method' => __METHOD__
						]);
						
						//$model->refresh();
					}
					
					$session->setFlash('success', \Yii::t('app', 'Created successfully.'));
					
					return $this->redirect(['view', 'id' => $model->id]);
				}
			}
			else
			{
				if ($this->_debugMode)
				{
					LogHelper::debug("Validation errors:\n" . print_r($model->errors, true), [
						'errors' => print_r($model->errors, true),
						'method' => __METHOD__
					]);
				}
			}
		}
		else
		{
			// Pre-populate default data
			$model->loadDefaultValues();
		}
		
		// Gather categoryTemplatesMap and categoryTypeMap data
		$allowedTemplatesJson = Json::encode($data['categoryTemplatesMap'] ?? []);
		$categoryTypeMapJson = Json::encode($data['categoryTypeMap'] ?? []);
		
		// For actionCreate only! Set current template and type JS vars from model (category_id already set!)
		$currentTemplateJs = Json::encode($model->template ?? '');
		$currentTypeJs = Json::encode(
			$data['categoryTypeMap'][$model->category_id] ?? null
		);
		
		// Render JS with updated values
		$this->renderJS($allowedTemplatesJson, $categoryTypeMapJson, $currentTemplateJs, $currentTypeJs);
		
		return $this->render('create', [
			'model' => $model,
			'allowedTemplatesByType' => Content::getAllAllowedTemplates(),
			'categoryOptions' => $data['categoryOptions'],
			'categoryTypeMap' => $data['categoryTypeMap'],
			'contentSummaryLength' => $this->_contentSummaryLength,
			'mediaBlogImageRecSize' => $this->_mediaBlogImageRecSize,
			'mediaProductImageRecSize' => $this->_mediaProductImageRecSize,
		]);
	}
	
	/**
	* Updates an existing model.
	* @param int $id ID
	* @return \yii\web\Response|string
	* @throws NotFoundHttpException if the model cannot be found
	*
	* Update is successful, the browser will be redirected to the 'view' page.
	*/
	public function actionUpdate(int $id): Response|string
	{
		$model = $this->findModel($id);
		
		// Early get category options for JS and form rendering
		$data = $this->getCategoryOptions(true);
		
		// Assign a default category_id if none is set (e.g. first in dropdown)
		if (!$model->category_id && !empty($data['categoryOptions']))
		{
			reset($data['categoryOptions']);
			$defaultCategoryId = key($data['categoryOptions']);
			$model->category_id = $defaultCategoryId;
		}
		
		$session = \Yii::$app->session;
		$request = \Yii::$app->request;
		
		if ($this->_debugMode)
		{
			LogHelper::debug('POST data (before load): ' . json_encode($request->post()), [
				'post data' => json_encode($request->post()),
				'method' => __METHOD__
			]);
		}
		
		if (($request->isPost) && ($model->load($request->post())))
		//if (($request->isPost) && ($model->load($request->post()) && $model->validate()))
		{
			if ($this->_debugMode)
			{
				LogHelper::debug('Model and POST data after loading:', [
					'model_attributes_json' => json_encode($model->attributes),
					'model_attributes_array' => $model->getAttributes(),
					'post_data' => $request->post(),
					'method' => __METHOD__,
				]);
			}
			
			// Determine scenario based on loaded data before validation
			$typeKey = $model->getTypeKey() ?: 'default';
			$combinedScenario = $typeKey . '_' . $model->template;
			
			$scenarios = $model->scenarios();
			
			if (isset($scenarios[$combinedScenario]))
			{
				$model->setScenario($combinedScenario);
			}
			elseif (isset($scenarios[$typeKey]))
			{
				$model->setScenario($typeKey);
			}
			else
			{
				$model->setScenario('default');
			}
			
			if ($this->_debugMode)
			{
				LogHelper::debug("Content update scenario and key attributes after scenario set", [
					'combinedScenario' => $combinedScenario,
					'currentScenario' => $model->getScenario(),
					'allScenarios' => implode(', ', array_keys($scenarios)),
					'keyAttributes' => [
						'type' => $model->type,
						'template' => $model->template,
						'category_id' => $model->category_id,
						'price' => $model->price,
						'purchase_options' => $model->purchase_options,
						'quantity' => $model->quantity,
						// Add more if needed
					],
					'method' => __METHOD__,
				]);
			}
			
			if ($model->validate())
			{
				if ($model->save())
				{
					if ($this->_debugMode)
					{
						LogHelper::debug('Saved content ID: ' . $model->id, [
							'model ID' => $model->id,
							//'price' => $model->price,// etc
							'method' => __METHOD__
						]);
						
						//$model->refresh();
					}
					
					$session->setFlash('success', \Yii::t('app', 'Updated successfully.'));
					
					return $this->redirect(['view', 'id' => $model->id]);
				}
			}
			else
			{
				if ($this->_debugMode)
				{
					LogHelper::debug("Validation errors:\n" . print_r($model->errors, true), [
						'errors' => print_r($model->errors, true),
						'method' => __METHOD__
					]);
				}
			}
		}
		
		// For actionUpdate only! Prepare renderJs() with category Type and Template data
		$allowedTemplatesJson = Json::encode($data['categoryTemplatesMap'] ?? []);
		$categoryTypeMapJson = Json::encode($data['categoryTypeMap'] ?? []);
		$currentTemplateJs = Json::encode($model->template ?? '');
		$currentTypeJs = Json::encode($model->type ?? null);
		
		// Render JS with updated values
		$this->renderJS($allowedTemplatesJson, $categoryTypeMapJson, $currentTemplateJs, $currentTypeJs);
		
		return $this->render('create', [
			'model' => $model,
			'allowedTemplatesByType' => Content::getAllAllowedTemplates(),
			'categoryOptions' => $data['categoryOptions'],
			'categoryTypeMap' => $data['categoryTypeMap'],
			'contentSummaryLength' => $this->_contentSummaryLength,
			'mediaBlogImageRecSize' => $this->_mediaBlogImageRecSize,
			'mediaProductImageRecSize' => $this->_mediaProductImageRecSize,
		]);
	}
	
	private function getCategoryOptions(bool $includeTypeMap = true): array
	{
		$categories = ContentCategory::find()->all();
		
		if (!$categories)
		{
			return [
				'categoryOptions' => [],
				'categoryTypeMap' => $includeTypeMap ? [] : null,
				'categoryTemplatesMap' => $includeTypeMap ? [] : null,
			];
		}
		
		// Build hierarchical and flat category list
		$hierarchicalCategories = ContentCategory::structureCategories($categories);
		$flatCategories = ContentCategory::flattenCategoriesDropdown($hierarchicalCategories);
		
		if (!is_array($flatCategories))
		{
			$flatCategories = [];
		}
		
		// Map integer type IDs to string keys expected in JS/toggle logic
		$typeLabels = [
			ContentCategory::TYPE_POST => 'post',
			ContentCategory::TYPE_PRODUCT => 'product',
			// Add other type mappings as needed
		];
		
		$categoryTypeMap = null;
		$categoryTemplatesMap = null;
		
		if ($includeTypeMap)
		{
			// Build categoryTypeMap and categoryTemplatesMap with type strings (not numeric IDs)
			$categoryTypeMap = [];
			$categoryTemplatesMap = [];
			
			foreach ($categories as $category)
			{
				$categoryTypeMap[$category->id] = $typeLabels[$category->type] ?? null;
			
				$allowedTemplates = $category->getAllowedTemplates();
				
				if (empty($allowedTemplates))
				{
					// fallback to global allowed templates by type
					$typeKey = $categoryTypeMap[$category->id] ?? '';
					$allowedTemplates = Content::getAllowedTemplatesByType($typeKey);
				}
				
				$categoryTemplatesMap[$category->id] = $allowedTemplates;
			}
		}
		
		return [
			'categoryOptions' => $flatCategories,
			'categoryTypeMap' => $categoryTypeMap,
			'categoryTemplatesMap' => $categoryTemplatesMap,
		];
	}
	
	protected function findModel(int $id): Content
	{
		if ($this->_isGuest)
		{
			return $this->goHome();
		}
		
		$model = Content::find()->id($id)->one();
		
		if ($model !== null)
		{
			return $model;
		}
		
		//throw new YiiWebNotFoundHttpException(ErrorMessageHelper::badRequest());
		throw new YiiWebNotFoundHttpException(
			\Yii::t('app', 'No Content found with `{value}`', ['value' => $id])
		);
	}
	
	/**
	* Handle Javascript and Caching
	*/
	private function renderJS(
		string $allowedTemplatesJson,
		string $categoryTypeMapJson,
		string $currentTemplateJs,
		string $currentTypeJs
	): void
	{
		$uniqueId = uniqid('', false);// Create 13-char unique string based on time
		$cacheKey = $this->_cacheKeyPrefix . $uniqueId . '_js_block_';
		$jsBlock = $this->getCachedJsBlock($cacheKey, function () use (
			$allowedTemplatesJson,
			$categoryTypeMapJson,
			$currentTemplateJs,
			$currentTypeJs
		)
		{
			/**
			* JavaScript
			* CSS Classes to control field visibility:
			* - Apply the class `.field-type-{type}` to all form fields shown for a specific content type,
			*   for example `.field-type-post` or `.field-type-product`.
			* - Apply the class `.field-template-{template}` to all form fields shown for a specific template,
			*   for example `.field-template-artwork` or `.field-template-exhibition`.
			* - Apply the class `.field-type-template-{type}-{template}` to form fields that should only be
			*   visible for the combined content type and template, for example `.field-type-template-product-artwork`.
			*
			* Examples:
			* Shows 'start_date' field only if content type is 'post':
			*
			* 	<?= $form->field($model, 'start_date', ['options' => ['class' => 'field-type-post']]); ?>
			*
			* Shows 'end_date' field only if template is 'exhibition' (regardless of type):
			* 
			* 	<?= $form->field($model, 'end_date', ['options' => ['class' => 'field-template-exhibition']]); ?>
			*
			* Shows 'artist' field only if content type is 'product' and template is 'artwork':
			* 
			*	<?= $form->field($model, 'artist', ['options' => [
			*		'class' => 'field-type-product field-template-artwork field-type-template-product-artwork'
			*	]]); ?>
			*/
			return <<<js
			(function(\$)
			{
				\$(function()
				{
					/* Map: category ID => array of allowed templates for that category 
						e.g. { "1": ["article", "exhibition"], "2": ["item", "artwork"] } */
					var categoryTemplatesMap = $allowedTemplatesJson;
					
					/* Map: category ID => content type string ('post', 'product', etc.) 
						e.g. { "1": "post", "2": "product" } */
					var categoryTypeMap = $categoryTypeMapJson;
					
					/* Initialize current template and type from PHP passed values, with fallback values */
					var currentTemplate = $currentTemplateJs || '';
					var currentType = $currentTypeJs || null;
					
					var \$category = \$('#content-category');
					var \$template = \$('#content-template');
					
					/* Given a category ID, return the content type as string ("post", "product", etc) */
					function getTypeForCategory(catId)
					{
						/* JSON keys are strings */
						catId = String(catId);
					
						return categoryTypeMap.hasOwnProperty(catId) ? categoryTypeMap[catId] : null;
					}
					
					/* Update template dropdown options based on category ID's allowed templates */
					function updateTemplateOptions(catId)
					{
						var templates = categoryTemplatesMap[catId] || [];
						\$template.empty();
						
						if (templates.length === 0)
						{
							/* No valid templates: disable dropdown and prompt user */
							\$template.append(\$('<option>').val('').text('Select a template'));
							\$template.prop('disabled', true);
						}
						else
						{
							/* Populate dropdown with templates, label capitalized for user friendliness */
							\$.each(templates, function(i, val)
							{
								var label = val.charAt(0).toUpperCase() + val.slice(1);
								\$template.append(\$('<option>').val(val).text(label));
							});
							
							\$template.prop('disabled', false);
						}
						
						/* Set the current template if it is present in the new options, else default to first option
							Array.prototype.includes is ES6+ */
						if (\$.inArray(currentTemplate, templates) !== -1)
						{
							\$template.val(currentTemplate);
						}
						else
						{
							\$template.val(templates[0] || '');
						}
					}
					
					/* Show/hide form fields based on the current type and template using class conventions */
					function toggleFields(type, template)
					{
						/*console.log('toggleFields called with:', type, template);*/
						
						/* Hide all fields that are tagged by any type or template CSS classes */
						\$('[class*="field-type-"]').hide();
						\$('[class*="field-template-"]').hide();
						\$('[class*="field-type-template-"]').hide();
						
						/* Show all fields matching current type e.g., '.field-type-post' */
						if (type)
						{
							\$('.field-type-' + type).show();
						}
						/* Show all fields matching current template e.g., '.field-template-artwork' */
						if (template)
						{
							\$('.field-template-' + template).show();
						}
						/* Show fields that require both specific type and template, e.g., '.field-type-template-product-artwork' */
						if (type && template)
						{
							\$('.field-type-template-' + type + '-' + template).show();
						}
					}
					
					/* Initialization on page load: */
					var initialCategoryId = \$category.val();
					var initialType = getTypeForCategory(initialCategoryId) || currentType;
					
					if (!categoryTemplatesMap || Object.keys(categoryTemplatesMap).length === 0 || !initialCategoryId)
					{
						/* No categories/templates data available OR no category selected:
							disable template dropdown & hide fields */
						\$template.empty()
							.append(\$('<option>').val('').text('Select a category first'))
							.prop('disabled', true);
						
						toggleFields(null, null);
					}
					else
					{
						/* Otherwise, fill templates for selected category and toggle fields accordingly */
						updateTemplateOptions(initialCategoryId);
						var initialTemplate = \$template.val();
						
						toggleFields(initialType, initialTemplate);
					}
					
					/* Event handler: when category selection changes, update type and templates accordingly */
					\$category.on('change', function()
					{
						var catId = \$(this).val();
						var type = getTypeForCategory(catId);
						
						if (!catId || !type)
						{
							/* No valid category or type: disable templates, hide all fields */
							\$template.empty()
								.append(\$('<option>').val('').text('Select a category first'))
								.prop('disabled', true);
							
							toggleFields(null, null);
							
							return;
						}
						
						/* Update globals so changes are tracked for template changes as well */
						currentType = type;
						currentTemplate = newTemplate;
					});
					
					/* Event handler: when template selection changes, toggle fields based on new template */
					\$template.on('change', function()
					{
						var template = \$(this).val();
						var type = getTypeForCategory(\$category.val()) || currentType;
						
						toggleFields(type, template);
						
						/* track current template for persistence */
						currentTemplate = template;
					});
				});
			})(jQuery);
			js;
		});
		
		$this->view->registerJs($jsBlock, View::POS_END);
	}
}
```
When creating new content template is stuck on "article" for all categories. This should not be the case, when updating a existing content,
template loads template properly. However if loading a existing template and I change the category, template disables and no longer can select any
templates.