1+ <?php
2+
3+ /**
4+ * Manages database connections and provides a unified interface for executing queries.
5+ * This class serves as a central point for managing multiple database connections,
6+ * facilitating the execution of queries and type conversion through registered type converters.
7+ *
8+ * @package CommonPHP
9+ * @subpackage DatabaseManager
10+ * @author Timothy McClatchey <timothy@commonphp.org>
11+ * @copyright 2024 CommonPHP.org
12+ * @license http://opensource.org/licenses/MIT MIT License
13+ * @noinspection PhpUnused
14+ */
15+
16+ namespace CommonPHP \DatabaseEngine ;
17+
18+ use CommonPHP \DatabaseEngine \Contracts \BuildableQueryContract ;
19+ use CommonPHP \DatabaseEngine \Contracts \ConnectorContract ;
20+ use CommonPHP \DatabaseEngine \Exceptions \ColumnNotFoundException ;
21+ use CommonPHP \DatabaseEngine \Exceptions \ConnectionAlreadyDefinedException ;
22+ use CommonPHP \DatabaseEngine \Exceptions \ConnectionNotRegisteredException ;
23+ use CommonPHP \DatabaseEngine \Exceptions \DatabaseEngineException ;
24+ use CommonPHP \DatabaseEngine \Exceptions \EmptyConnectionNameException ;
25+ use CommonPHP \DatabaseEngine \Exceptions \ResultNotReadException ;
26+ use CommonPHP \DatabaseEngine \Support \TypeConversionProvider ;
27+ use CommonPHP \DatabaseEngine \TypeConverters \BoolTypeConverter ;
28+ use CommonPHP \DatabaseEngine \TypeConverters \DateTimeTypeConverter ;
29+ use CommonPHP \DatabaseEngine \TypeConverters \FloatTypeConverter ;
30+ use CommonPHP \DatabaseEngine \TypeConverters \IntTypeConverter ;
31+ use CommonPHP \DatabaseEngine \TypeConverters \MixedTypeConverter ;
32+ use CommonPHP \DatabaseEngine \TypeConverters \StringTypeConverter ;
33+ use DateTime ;
34+
35+ final class ConnectionManager
36+ {
37+ /**
38+ * @var ConnectorContract[] Holds the registered database connections.
39+ */
40+ private array $ connections = [];
41+
42+ /**
43+ * Provides type conversion services for database values.
44+ *
45+ * @var TypeConversionProvider
46+ */
47+ public readonly TypeConversionProvider $ typeConversionProvider ;
48+
49+ /**
50+ * Initializes the ConnectionManager with an optional TypeConversionProvider.
51+ * Registers default type converters if not already supported by the provided TypeConversionProvider.
52+ *
53+ * @param TypeConversionProvider|null $typeConversionProvider The type conversion provider to use. A new instance is created if null is passed.
54+ */
55+ public function __construct (?TypeConversionProvider $ typeConversionProvider = null )
56+ {
57+ if ($ typeConversionProvider === null )
58+ {
59+ $ typeConversionProvider = new TypeConversionProvider ();
60+ }
61+ if (!$ typeConversionProvider ->supportsType ('bool ' )) $ typeConversionProvider ->register ('bool ' , new BoolTypeConverter ());
62+ if (!$ typeConversionProvider ->supportsType (DateTime::class)) $ typeConversionProvider ->register (DateTime::class, new DateTimeTypeConverter ());
63+ if (!$ typeConversionProvider ->supportsType ('float ' )) $ typeConversionProvider ->register ('float ' , new FloatTypeConverter ());
64+ if (!$ typeConversionProvider ->supportsType ('int ' )) $ typeConversionProvider ->register ('int ' , new IntTypeConverter ());
65+ if (!$ typeConversionProvider ->supportsType ('mixed ' )) $ typeConversionProvider ->register ('mixed ' , new MixedTypeConverter ());
66+ if (!$ typeConversionProvider ->supportsType ('string ' )) $ typeConversionProvider ->register ('string ' , new StringTypeConverter ());
67+ $ this ->typeConversionProvider = $ typeConversionProvider ;
68+ }
69+
70+ /**
71+ * Retrieves a registered connector by name.
72+ *
73+ * @param string $connectionName The name of the connection to retrieve.
74+ * @return ConnectorContract The requested connector.
75+ * @throws ConnectionNotRegisteredException If the connection name is not registered.
76+ */
77+ public function with (string $ connectionName ): ConnectorContract
78+ {
79+ $ name = trim (strtolower ($ connectionName ));
80+ if (!isset ($ this ->connections [$ name ]))
81+ {
82+ throw new ConnectionNotRegisteredException ($ connectionName );
83+ }
84+ return $ this ->connections [$ name ];
85+ }
86+
87+ /**
88+ * Registers a new database connection under a specified name.
89+ *
90+ * @param string $connectionName The name to register the connection under.
91+ * @param ConnectorContract $connection The connector instance to register.
92+ * @throws EmptyConnectionNameException If the provided connection name is empty.
93+ * @throws ConnectionAlreadyDefinedException If a connection with the given name is already registered.
94+ */
95+ public function register (string $ connectionName , ConnectorContract $ connection ): void
96+ {
97+ $ name = trim (strtolower ($ connectionName ));
98+ if ($ name == '' )
99+ {
100+ throw new EmptyConnectionNameException ();
101+ }
102+ if (isset ($ this ->connections [$ name ]))
103+ {
104+ throw new ConnectionAlreadyDefinedException ($ connectionName );
105+ }
106+ $ this ->connections [$ name ] = $ connection ;
107+ }
108+
109+ /**
110+ * Retrieves the last insert ID from the specified connection.
111+ *
112+ * @param string $connectionName The name of the connection to retrieve the last insert ID from.
113+ * @return string|int The last insert ID as a string or integer, depending on the database.
114+ * @throws ConnectionNotRegisteredException
115+ */
116+ public function getLastInsertId (string $ connectionName ): string |int
117+ {
118+ return $ this ->with ($ connectionName )->getLastInsertId ();
119+ }
120+
121+ /**
122+ * Executes a non-query (e.g., INSERT, UPDATE, DELETE) using the specified connection.
123+ *
124+ * @param string $connectionName The name of the connection to use for execution.
125+ * @param Query|BuildableQueryContract $query The query to execute.
126+ * @return int The number of rows affected by the query.
127+ * @throws ConnectionNotRegisteredException
128+ */
129+ public function executeNonQuery (string $ connectionName , Query |BuildableQueryContract $ query ): int
130+ {
131+ return $ this ->execute ($ connectionName , $ query )->getRowCount ();
132+ }
133+
134+ /**
135+ * Executes a query that returns a single scalar value using the specified connection.
136+ *
137+ * @template T
138+ * @param string $connectionName The name of the connection to use for execution.
139+ * @param Query|BuildableQueryContract $query The query to execute.
140+ * @param class-string<T> $expectedType The expected return type of the query result.
141+ * @return T The scalar value returned by the query, converted to the specified type.
142+ * @throws ColumnNotFoundException
143+ * @throws ConnectionNotRegisteredException
144+ * @throws DatabaseEngineException
145+ * @throws ResultNotReadException
146+ */
147+ public function executeScalar (string $ connectionName , Query |BuildableQueryContract $ query , string $ expectedType = 'mixed ' ): mixed
148+ {
149+ $ scalar = null ;
150+ $ result = $ this ->execute ($ connectionName , $ query );
151+ if ($ result ->read () && $ result ->getColumnCount () > 0 )
152+ {
153+ $ scalar = $ result ->getValue (0 , $ expectedType );
154+ }
155+ return $ scalar ;
156+ }
157+
158+ /**
159+ * Executes a query using the specified connection and returns the result set.
160+ *
161+ * @param string $connectionName The name of the connection to use for execution.
162+ * @param Query|BuildableQueryContract $query The query to execute.
163+ * @return Result The result set of the executed query.
164+ * @throws ConnectionNotRegisteredException
165+ */
166+ public function execute (string $ connectionName , Query |BuildableQueryContract $ query ): Result
167+ {
168+ return $ this ->with ($ connectionName )->execute ($ this ->typeConversionProvider , $ query );
169+ }
170+ }
0 commit comments