The code:
SessionInterface
SessionManager
SessionUser
SessionCart
ServiceCart

SessionManager uses yii2 session interface to interact together:
```
interface SessionInterface
{
	public function getId(): string;
	public function set(string $key, $value): void;
	public function get(string $key);
	public function has(string $key): bool;
	public function remove(string $key): void;
	public function destroy(): void;
	public function isActive(): bool;
}

class SessionManager implements SessionInterface
{
	public function getId(): string
	{
		$this->validateSession();// Ensure the session is open.
		
		return \Yii::$app->session->getId();
	}
	
	public function set(string $key, $value): void
	{
		$this->validateSession();// Ensure the session is open.
		
		\Yii::$app->session->set($key, $value);
	}
	
	public function get(string $key)
	{
		$this->validateSession();// Ensure the session is open.
		
		return \Yii::$app->session->get($key);
	}
	
	public function has(string $key): bool
	{
		$this->validateSession();// Ensure the session is open.
		
		return \Yii::$app->session->has($key);
	}
	
	public function remove(string $key): void
	{
		$this->validateSession();// Ensure the session is open.
		
		\Yii::$app->session->remove($key);
	}
	
	public function destroy(): void
	{
		$this->validateSession();// Ensure the session is open.
		
		\Yii::$app->session->destroy();
	}
	
	public function isActive(): bool
	{
		return \Yii::$app->session->isActive;
	}
	
	private function validateSession(): void
	{
		if (!$this->isActive())
		{
			\Yii::$app->session->open();
		}
	}
}
```

SessionUser is a ActiveRecorder model that "SessionUser represents a user session stored in DB with enhanced security features."
Here is the write up on it:

```
/**
* @property int $id
* @property string $session_id
* @property string|null $session_token
* @property string|null $user_ip
* @property string|null $user_agent
* @property int $created_at
* @property int|null $updated_at
* @property string|null $extra_data
*
* If you want to avoid creating a new SessionManager instance every time, you could also 
* bootstrap the dependency through the Yii application component:
*
* First, register your SessionManager component in your application 
* configuration (usually in config/web.php or config/console.php):
* 	
* 	'components' => [
* 		'sessionManager' => [
* 			'class' => 'app\components\SessionManager',
* 		],
* 		'sessionUser' => [
*			'class' => \app\models\SessionUser::class,
*			'sessionManager' => \Yii::$app->sessionManager,// inject existing component
*		],
* 	],
*
* This way, you can get the session user anywhere via:
*
* 	$sessionUser = \Yii::$app->sessionUser;
*
* Use a base controller that all your controllers extend:
*
* 	namespace app\controllers;
* 	
* 	use yii\web\Controller;
* 	use app\components\SessionManager;
* 	use app\models\SessionUser;
* 	
* 	class BaseController extends Controller
* 	{
* 		protected SessionUser $_sessionUser;
* 		
* 		public function init()
* 		{
* 			parent::init();// __construct()
*			
* 			$sessionManager = new SessionManager();
* 			$this->_sessionUser = new SessionUser($sessionManager);
* 			
* 			// Optionally, do initial session validation/load here
* 		}
* 	}
* 	
* 	public function beforeAction($action)
* 	{
* 		if (parent::beforeAction($action))
*		{
* 				
* 			$sessionData = null;
* 			try
*			{
* 				$sessionData = $this->_sessionUser->readSession();
* 			}
* 			catch (\Throwable $e)
*			{
* 				\Yii::error('Error reading session data: ' . $e->getMessage());
* 			}
* 			if (!$sessionData)
*			{
* 				$this->_sessionUser->writeSession([
* 					'extra_data' => ['order_id' => '123'],
* 					// additional default values if needed...
* 				]);
* 			}
*			return true;
* 		}	
* 		// If parent::beforeAction returns false, deny access.
* 		return false;
* 	}
*
* Usage example to read session:
* 
* 	$sessionUser = new SessionUser();
* 	$data = $sessionUser->readSession();
* 	if ($data === null)
*	{
* 		// Session invalid — user should reauthenticate or session cleared
* 	}
*/
```

SessionUser works well and needs no changes. SessionCart and ServiceCart are the goal today.
SessionCart writeup:

```
/**
* SessionCart manages cart functionality by interacting with SessionUser's cart_data.
* It is a thin layer focused on cart operations while inheriting full session security.
*
* Usage:
*
* 	$sessionUser = new SessionUser();
*	$sessionCart = new SessionCart($sessionUser);
*
* Add 3 units of product with id=101
*
*	$success = $sessionCart->addProductToCart(101, 3);
*
* Fetch all items
* 
*	$items = $sessionCart->getCartItems();
*
* Get cart quantity and total
*
*	$quantity = $sessionCart->getCartQuantity();
*	$total = $sessionCart->getCartTotal();
*/
class SessionCart
{
	private SessionUser $_sessionUser;
	
	public function __construct(SessionUser $sessionUser)
	{
		$this->_sessionUser = $sessionUser;
	}
	
	public function getCartItems(): array
	{
		
	
		$sessionData = $this->_sessionUser->readSession();
		if ($sessionData === null)
		{
			return [];
		}
		
		return $sessionData['cart_data'] ?? [];
	}
	
	private function checkInventory(): array
	{
		$items = $this->getCartItems();
		
		if (empty($items))
		{
			return [];
		}
		
		foreach ($items as $index => &$item)
		{
			$productId = (int) ($item['id'] ?? 0);
			
			if ($productId <= 0)
			{
				unset($items[$index]);
				
				continue;
			}
			
			// @var Product|null $product
			$product = Product::find()->id($productId)->published()->one();
			
			if (!$product)
			{
				unset($items[$index]);
				
				continue;
			}
			
			$availableQty = (int) $product->quantity;
			
			// Adjust cart quantity if exceeds available stock
			if (($item['quantity'] ?? 0) > $availableQty)
			{
				$item['quantity'] = $availableQty;
			}
			
			$item['available_quantity'] = $availableQty;
			$item['price'] = (float) $product->price;
			$item['title'] = $product->title;
			$item['total_price'] = round($item['quantity'] * $item['price'], 2);
			
			// Add any other product info you want here (e.g., image, slug)
		}
		
		unset($item);
		
		// Optionally save adjusted quantities back to sessionUser
		$this->_sessionUser->writeSession(['cart_data' => $items]);
		
		return $items;
	}
	
	public function addItem(int $productId, int $quantity): bool
	{
		if ($quantity <= 0)
		{
			return false;
		}
		
		$sessionData = $this->_sessionUser->readSession();
		
		if ($sessionData === null)
		{
			return false;
		}
		
		$cartData = $sessionData['cart_data'] ?? [];
		$found = false;
		
		foreach ($cartData as &$item)
		{
			if (($item['id'] ?? null) === $productId)
			{
				$item['quantity'] += $quantity;
				$found = true;
				
				break;
			}
		}
		
		unset($item);
		
		if (!$found)
		{
			$cartData[] = ['id' => $productId, 'quantity' => $quantity];
		}
		
		return $this->_sessionUser->writeSession(['cart_data' => $cartData]);
	}
	
	public function removeItem(int $productId): bool
	{
		$sessionData = $this->_sessionUser->readSession();
		
		if ($sessionData === null)
		{
			return false;
		}
		
		$cartData = $sessionData['cart_data'] ?? [];
		$originalCount = count($cartData);
		
		// Remove all cart items matching the given product ID from the cart data array
		$cartData = array_filter($cartData, fn($item) => ($item['id'] ?? null) !== $productId);
		
		// product not found
		if (count($cartData) === $originalCount)
		{
			return false;
		}
		
		return $this->_sessionUser->writeSession(['cart_data' => array_values($cartData)]);
	}
	
	public function clearCart(): bool
	{
		return $this->_sessionUser->writeSession(['cart_data' => []]);
	}
	
	public function getCartQuantity(): ?int
	{
		$items = $this->getCartItems();
		
		if (empty($items))
		{
			return null;
		}
		
		$sum = 0;
		
		foreach ($items as $item)
		{
			$sum += (int) ($item['quantity'] ?? 0);
		}
		
		return $sum > 0 ? $sum : null;
	}
	
	public function getCartTotal(): ?float
	{
		$items = $this->getCartItems();
		
		if (empty($items))
		{
			return null;
		}
		
		$sum = 0.0;
		
		foreach ($items as $item)
		{
			// If you've stored total_price per item, use it. Otherwise, integrate with product prices.
			$sum += (float) ($item['total_price'] ?? 0);
		}
		
		return $sum > 0 ? round($sum, 2) : null;
	}
}

When using getCartItems() it should first checkInventory() I wanted to make sure that this would work well. Thoughts?