### Install and Configure AuraSqlProfileModule Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Install AuraSqlProfileModule to log SQL execution. This example binds a minimal function logger created in an anonymous class for PSR-3 logging. ```php class DevModule extends AbstractModule { protected function configure() { // ... $this->install(new AuraSqlProfileModule()); $this->bind(LoggerInterface::class)->toInstance( new class extends AbstractLogger { /** @inheritDoc */ public function log($level, $message, array $context = []) { $replace = []; foreach ($context as $key => $val) { if (! is_array($val) && (! is_object($val) || method_exists($val, '__toString'))) { $replace['{' . $key . '}'] = $val; } } error_log(strtr($message, $replace)); } } ); } } ``` -------------------------------- ### Install Ray.AuraSqlModule with Composer Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Use this command to install the module via Composer. ```bash composer require ray/aura-sql-module ``` -------------------------------- ### Configure SQL Logging with AuraSqlProfileModule Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Install the profile module and bind a PSR-3 compatible logger to capture SQL queries and execution context. ```php install(new AuraSqlModule('mysql:host=localhost;dbname=myapp', 'user', 'pass')); $this->install(new AuraSqlProfileModule()); // Bind a PSR-3 logger for SQL profiling $this->bind(LoggerInterface::class)->toInstance( new class extends AbstractLogger { public function log($level, $message, array $context = []): void { $replace = []; foreach ($context as $key => $val) { if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { $replace['{' . $key . '}'] = $val; } } $formattedMessage = strtr($message, $replace); error_log(sprintf('[SQL %s] %s', strtoupper($level), $formattedMessage)); } } ); } } ``` -------------------------------- ### Install Rector for Migration Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/ANNOTATION_TO_ATTRIBUTE.md Install the Rector tool as a development dependency to automate the conversion process. ```bash composer require --dev rector/rector ``` -------------------------------- ### Install AuraSqlEnvModule using Environment Variables Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Use AuraSqlEnvModule to fetch database connection details from environment variables at runtime. This is recommended for production environments. ```php $this->install( new AuraSqlEnvModule( 'PDO_DSN', 'PDO_USER', 'PDO_PASSWORD', 'PDO_SLAVE', $options, // optional key=>value array of driver-specific connection options $queries // Queries to execute after the connection. ) ); ``` -------------------------------- ### Install AuraSqlModule in AppModule Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Install the AuraSqlModule by providing connection details directly. This is suitable for development or when connection parameters are static. ```php use Ray\Di\AbstractModule; use Ray\AuraSqlModule\AuraSqlModule; use Ray\AuraSqlModule\AuraSqlQueryModule; class AppModule extends AbstractModule { protected function configure() { $this->install( new AuraSqlModule( 'mysql:host=localhost;dbname=test', 'username', 'password', 'slave1,slave2,slave3', // optional slave server list $options, // optional key=>value array of driver-specific connection options $queries // Queries to execute after the connection. ) ); } } ``` -------------------------------- ### Inject Named PDO Instance with NamedPdoModule Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Use NamedPdoModule to inject distinct PDO instances, aliased by a qualifier, for non-replication scenarios. This example installs a 'log_db' named PDO. ```php class AppModule extends AbstractModule { protected function configure() { $this->install(new NamedPdoModule('log_db', 'mysql:host=localhost;dbname=log', 'username', 'password')); } } ``` -------------------------------- ### Read-Only Connection Annotation Migration Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/ANNOTATION_TO_ATTRIBUTE.md Example showing the conversion of the ReadOnlyConnection annotation on a constructor. ```php use Aura\\\Sql\\\ExtendedPdoInterface; use Ray\\\AuraSqlModule\\\Annotation\\\ReadOnlyConnection; class UserFinder { /** * @ReadOnlyConnection */ public function __construct( private ExtendedPdoInterface $pdo ) {} } ``` ```php use Aura\\\Sql\\\ExtendedPdoInterface; use Ray\\\AuraSqlModule\\\Annotation\\\ReadOnlyConnection; class UserFinder { #[ReadOnlyConnection] public function __construct( private ExtendedPdoInterface $pdo ) {} } ``` -------------------------------- ### Configure AuraSqlQueryModule with Ray.Di Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Install AuraSqlModule and AuraSqlQueryModule in your Ray.Di module configuration. Specify the database connection string for AuraSqlModule and the database type (e.g., 'mysql') for AuraSqlQueryModule. ```php install(new AuraSqlModule('mysql:host=localhost;dbname=myapp', 'user', 'pass')); $this->install(new AuraSqlQueryModule('mysql')); // mysql, pgsql, sqlite, or sqlsrv } } ``` -------------------------------- ### Transactional Annotation Migration Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/ANNOTATION_TO_ATTRIBUTE.md Example showing the conversion of Transactional and WriteConnection annotations to PHP 8 attributes. ```php use Ray\\\AuraSqlModule\\\Annotation\\\Transactional; use Ray\\\AuraSqlModule\\\Annotation\\\WriteConnection; class UserRepository { /** * @WriteConnection * @Transactional */ public function save(User $user): void { // ... } } ``` ```php use Ray\\\AuraSqlModule\\\Annotation\\\Transactional; use Ray\\\AuraSqlModule\\\Annotation\\\WriteConnection; class UserRepository { #[WriteConnection, Transactional] public function save(User $user): void { // ... } } ``` -------------------------------- ### Basic Database Connection with AuraSqlModule Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Configure a single database connection with optional slave server support for read operations. Use this for simple setups or when environment variables are not preferred. ```php install( new AuraSqlModule( 'mysql:host=localhost;dbname=myapp', // DSN 'username', // Username 'password', // Password 'slave1,slave2,slave3', // Optional: comma-separated slave hosts ['PDO::ATTR_ERRMODE' => PDO::ERRMODE_EXCEPTION], // Optional: PDO options ['SET NAMES utf8mb4'] // Optional: queries after connection ) ); } } // Usage $injector = new Injector(new AppModule()); $pdo = $injector->getInstance(ExtendedPdoInterface::class); // Execute queries using ExtendedPdo $users = $pdo->fetchAll('SELECT * FROM users WHERE active = :active', ['active' => 1]); $user = $pdo->fetchOne('SELECT * FROM users WHERE id = :id', ['id' => 42]); // Insert with automatic parameter binding $pdo->perform( 'INSERT INTO users (name, email) VALUES (:name, :email)', ['name' => 'John Doe', 'email' => 'john@example.com'] ); $lastId = $pdo->lastInsertId(); ``` -------------------------------- ### Master/Slave Replication with AuraSqlReplicationModule Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Configure automatic read/write splitting based on HTTP method. GET requests use slave connections, while other methods use the master connection. ```php setWrite('master', new Connection( 'mysql:host=master.db.local;dbname=myapp', 'write_user', 'write_pass' )); // Configure slave (read) connections $locator->setRead('slave1', new Connection( 'mysql:host=slave1.db.local;dbname=myapp', 'read_user', 'read_pass' )); $locator->setRead('slave2', new Connection( 'mysql:host=slave2.db.local;dbname=myapp', 'read_user', 'read_pass' )); $this->install(new AuraSqlReplicationModule($locator)); } } // Automatic connection selection based on HTTP method: // - GET requests → slave connection (read) // - POST/PUT/DELETE requests → master connection (write) ``` -------------------------------- ### Configure AuraSqlReplicationModule with Qualifier for Named DB Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Set a qualifier when installing AuraSqlReplicationModule to manage named database connections, particularly when dealing with replication for specific databases like a 'log_db'. ```php class AppModule extends AbstractModule { protected function configure() { $this->install(new AuraSqlReplicationModule($locator, 'log_db')); } } ``` -------------------------------- ### Implement Transactional Method with AuraSqlModule Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Mark methods with the `#[Transactional]` attribute to automatically manage database transactions. The module handles starting the transaction before execution and committing or rolling back based on success or exceptions. ```php use Ray\AuraSqlModule\Annotation\WriteConnection; // important use Ray\AuraSqlModule\Annotation\Transactional; // important class User { public $pdo; #[WriteConnection, Transactional] public function write() { // $this->pdo->rollback(); when exception thrown. } } ``` -------------------------------- ### Environment-Based Configuration with AuraSqlEnvModule Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Retrieve database credentials from environment variables at runtime. Ideal for containerized deployments and 12-factor applications. ```php install( new AuraSqlEnvModule( 'PDO_DSN', // getenv('PDO_DSN') 'PDO_USER', // getenv('PDO_USER') 'PDO_PASSWORD', // getenv('PDO_PASSWORD') 'PDO_SLAVE', // getenv('PDO_SLAVE') - optional [], // PDO options [] // Post-connection queries ) ); } } ``` -------------------------------- ### Run PHPUnit tests Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/ANNOTATION_TO_ATTRIBUTE.md Execute the test suite to verify the migration. ```bash vendor/bin/phpunit ``` -------------------------------- ### Configure AuraSqlReplicationModule with ConnectionLocator Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Set up AuraSqlReplicationModule using a ConnectionLocator for master/slave database connections. This allows for read/write splitting. ```php use Ray\Di\AbstractModule; use Ray\AuraSqlModule\AuraSqlModule; use Aura\Sql\ConnectionLocator; use Aura\Sql\Connection; class AppModule extends AbstractModule { protected function configure() { $locator = new ConnectionLocator; $locator->setWrite('master', new Connection('mysql:host=localhost;dbname=master', 'id', 'pass')); $locator->setRead('slave1', new Connection('mysql:host=localhost;dbname=slave1', 'id', 'pass')); $locator->setRead('slave2', new Connection('mysql:host=localhost;dbname=slave2', 'id', 'pass')); $this->install(new AuraSqlReplicationModule($locator)); } } ``` -------------------------------- ### Injecting SelectInterface for Query Building Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Demonstrates injecting SelectInterface into a repository to build and execute SELECT queries. ```php use Aura\SqlQuery\Common\SelectInterface; use Aura\Sql\ExtendedPdoInterface; class UserRepository { public function __construct( private readonly SelectInterface $select, private readonly ExtendedPdoInterface $pdo ) {} public function findById(int $id): array { $statement = $this->select ->distinct() // SELECT DISTINCT ->cols([ // select these columns 'id', // column name 'name AS namecol', // one way of aliasing 'col_name' => 'col_alias', // another way of aliasing 'COUNT(foo) AS foo_count' // embed calculations directly ]) ->from('users AS u') // FROM these tables ->where('id = :id') ->getStatement(); return $this->pdo->fetchAssoc($statement, ['id' => $id]); } } ``` -------------------------------- ### Using Multiple Query Builders Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Shows how to inject and use InsertInterface and UpdateInterface alongside SelectInterface for CRUD operations. ```php use Aura\SqlQuery\Common\SelectInterface; use Aura\SqlQuery\Common\InsertInterface; use Aura\SqlQuery\Common\UpdateInterface; use Aura\Sql\ExtendedPdoInterface; class UserService { public function __construct( private readonly SelectInterface $select, private readonly InsertInterface $insert, private readonly UpdateInterface $update, private readonly ExtendedPdoInterface $pdo ) {} public function createUser(array $userData): int { $statement = $this->insert ->into('users') ->cols($userData) ->getStatement(); $this->pdo->perform($statement, $this->insert->getBindValues()); return (int) $this->pdo->lastInsertId(); } public function updateUser(int $id, array $userData): bool { $statement = $this->update ->table('users') ->cols($userData) ->where('id = :id') ->bindValue('id', $id) ->getStatement(); return $this->pdo->perform($statement, $this->update->getBindValues()); } } ``` -------------------------------- ### Pagination with ExtendedPdo Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Uses AuraSqlPagerFactoryInterface to paginate raw SQL queries. ```php use Ray\AuraSqlModule\Pagerfanta\AuraSqlPagerFactoryInterface; use Aura\Sql\ExtendedPdoInterface; class UserListService { public function __construct( private readonly AuraSqlPagerFactoryInterface $pagerFactory, private readonly ExtendedPdoInterface $pdo ) {} public function getUserList(int $page): Page { $sql = 'SELECT * FROM users WHERE active = :active'; $params = ['active' => 1]; $pager = $this->pagerFactory->newInstance($this->pdo, $sql, $params, 10, '/?page={page}&category=users'); return $pager[$page]; } } ``` -------------------------------- ### Verify Fully Qualified Class Names Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/ANNOTATION_TO_ATTRIBUTE.md Ensure imports use fully qualified class names so Rector can correctly identify the annotations. ```php // Correct use Ray\AuraSqlModule\Annotation\Transactional; // Incorrect - Rector won't recognize this use Ray\AuraSqlModule\Annotation as DB; ``` -------------------------------- ### Configure Multiple Named Database Connections with PHP Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Use NamedPdoModule and NamedPdoEnvModule to define and inject multiple named database connections. The default AuraSqlModule binds the primary connection without a qualifier. ```php install(new AuraSqlModule( 'mysql:host=localhost;dbname=main', 'user', 'pass' )); // Log database with qualifier $this->install(new NamedPdoModule( 'log_db', 'mysql:host=localhost;dbname=logs', 'log_user', 'log_pass' )); // Analytics database from environment $this->install(new NamedPdoEnvModule( 'analytics_db', 'ANALYTICS_DSN', 'ANALYTICS_USER', 'ANALYTICS_PASSWORD' )); } } class UserService { private ExtendedPdoInterface $mainDb; private ExtendedPdoInterface $logDb; private ExtendedPdoInterface $analyticsDb; #[Inject] public function setDatabases( ExtendedPdoInterface $mainDb, #[Named('log_db')] ExtendedPdoInterface $logDb, #[Named('analytics_db')] ExtendedPdoInterface $analyticsDb ) { $this->mainDb = $mainDb; $this->logDb = $logDb; $this->analyticsDb = $analyticsDb; } public function createUser(array $data): int { $this->mainDb->perform( 'INSERT INTO users (name, email) VALUES (:name, :email)', $data ); $userId = (int) $this->mainDb->lastInsertId(); // Log to separate database $this->logDb->perform( 'INSERT INTO audit_log (action, user_id, created_at) VALUES (:action, :user_id, NOW())', ['action' => 'user_created', 'user_id' => $userId] ); return $userId; } } ``` -------------------------------- ### Pagination with Select Query Builder Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Uses AuraSqlQueryPagerFactoryInterface to paginate SelectInterface query objects. ```php use Ray\AuraSqlModule\Pagerfanta\AuraSqlQueryPagerFactoryInterface; use Aura\SqlQuery\Common\SelectInterface; use Aura\Sql\ExtendedPdoInterface; class ProductListService { public function __construct( private readonly AuraSqlQueryPagerFactoryInterface $queryPagerFactory, private readonly SelectInterface $select, private readonly ExtendedPdoInterface $pdo ) {} public function getProductList(int $page, string $category): Page { $select = $this->select ->from('products') ->where('category = :category') ->bindValue('category', $category); $pager = $this->queryPagerFactory->newInstance($this->pdo, $select, 10, '/?page={page}&category=' . $category); return $pager[$page]; } } ``` -------------------------------- ### Compare Doctrine Annotations and PHP 8 Attributes Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Shows the syntax difference between legacy Doctrine annotations and modern PHP 8 attributes for class methods. ```php bind(TemplateInterface::class)->to(TwitterBootstrap3Template::class); $this->bind()->annotatedWith(PagerViewOption::class)->toInstance($pagerViewOption); ``` -------------------------------- ### Migrate to PHP 8 Attributes using Rector Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Use Rector CLI commands to automate the migration of Doctrine annotations to native PHP 8 attributes. ```bash # Install Rector if not already installed composer require --dev rector/rector # Preview migration changes (dry-run) vendor/bin/rector process src --config=vendor/ray/aura-sql-module/rector-migrate.php --dry-run # Apply migration vendor/bin/rector process src --config=vendor/ray/aura-sql-module/rector-migrate.php # Migrate test files too vendor/bin/rector process tests --config=vendor/ray/aura-sql-module/rector-migrate.php # Remove doctrine/annotations dependency after migration composer remove doctrine/annotations ``` -------------------------------- ### Use WriteConnection for Write Operations Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Employ the `#[WriteConnection]` attribute for methods like `updateStock` and `create` to guarantee they utilize the write database connection, even if the HTTP request method is typically associated with reading. ```php pdo = $pdo; } #[WriteConnection] public function updateStock(int $productId, int $quantity): bool { // Always uses master/write connection even during GET request return (bool) $this->pdo->perform( 'UPDATE products SET stock = :quantity, updated_at = NOW() WHERE id = :id', ['quantity' => $quantity, 'id' => $productId] ); } #[WriteConnection, Transactional] public function create(array $data): int { // Uses write connection with transaction support $this->pdo->perform( 'INSERT INTO products (name, price, stock, category_id) VALUES (:name, :price, :stock, :category_id)', $data ); return (int) $this->pdo->lastInsertId(); } } ``` -------------------------------- ### UserRepository with AuraSqlQueryModule Dependencies Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Inject Aura.SqlQuery interfaces (SelectInterface, InsertInterface, UpdateInterface, DeleteInterface) and ExtendedPdoInterface into your repository class. These are used for building and executing SQL queries. ```php class UserRepository { public function __construct( private readonly SelectInterface $select, private readonly InsertInterface $insert, private readonly UpdateInterface $update, private readonly DeleteInterface $delete, private readonly ExtendedPdoInterface $pdo ) {} public function findWithFilters(array $filters): array { $this->select ->cols(['id', 'name', 'email', 'created_at']) ->from('users') ->where('active = :active') ->bindValue('active', 1); if (isset($filters['name'])) { $this->select ->where('name LIKE :name') ->bindValue('name', '%' . $filters['name'] . '%'); } if (isset($filters['since'])) { $this->select ->where('created_at >= :since') ->bindValue('since', $filters['since']); } $this->select->orderBy(['created_at DESC'])->limit(50); return $this->pdo->fetchAll( $this->select->getStatement(), $this->select->getBindValues() ); } public function create(array $userData): int { $this->insert ->into('users') ->cols([ 'name' => $userData['name'], 'email' => $userData['email'], 'password_hash' => password_hash($userData['password'], PASSWORD_DEFAULT), 'created_at' => date('Y-m-d H:i:s') ]); $this->pdo->perform( $this->insert->getStatement(), $this->insert->getBindValues() ); return (int) $this->pdo->lastInsertId(); } public function updateEmail(int $userId, string $newEmail): bool { $this->update ->table('users') ->cols(['email' => $newEmail, 'updated_at' => date('Y-m-d H:i:s')]) ->where('id = :id') ->bindValue('id', $userId); return (bool) $this->pdo->perform( $this->update->getStatement(), $this->update->getBindValues() ); } public function softDelete(int $userId): bool { $this->update ->table('users') ->cols(['deleted_at' => date('Y-m-d H:i:s'), 'active' => 0]) ->where('id = :id') ->bindValue('id', $userId); return (bool) $this->pdo->perform( $this->update->getStatement(), $this->update->getBindValues() ); } } ``` -------------------------------- ### Accessing Page Object Properties Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Demonstrates properties available on the Page object returned by the pager. ```php /* @var Pager \Ray\AuraSqlModule\Pagerfanta\Page */ // $page->data // sliced data // $page->current; // $page->total // $page->hasNext // $page->hasPrevious // $page->maxPerPage; // (string) $page // pager html ``` -------------------------------- ### Paginate Aura.SqlQuery builders with AuraSqlQueryPagerFactory Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Use AuraSqlQueryPagerFactoryInterface to paginate SelectInterface query builders. This approach allows for dynamic query construction before passing the builder to the factory. ```php select ->cols(['id', 'name', 'description', 'price', 'image_url', 'stock']) ->from('products') ->where('active = 1') ->where('name LIKE :query OR description LIKE :query') ->bindValue('query', '%' . $query . '%'); if ($minPrice !== null) { $this->select ->where('price >= :min_price') ->bindValue('min_price', $minPrice); } if ($maxPrice !== null) { $this->select ->where('price <= :max_price') ->bindValue('max_price', $maxPrice); } $this->select->orderBy(['relevance DESC', 'created_at DESC']); $uriTemplate = sprintf( '/search?q=%s&min=%s&max=%s&page={page}', urlencode($query), $minPrice ?? '', $maxPrice ?? '' ); $pager = $this->queryPagerFactory->newInstance( $this->pdo, $this->select, $perPage, $uriTemplate ); return $pager[$page]; } } ``` -------------------------------- ### Run Automated Migration with Rector Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/ANNOTATION_TO_ATTRIBUTE.md Use the provided Rector configuration to process source code or tests. The dry-run flag allows previewing changes before applying them. ```bash # Dry-run to preview changes vendor/bin/rector process src --config=vendor/ray/aura-sql-module/rector-migrate.php --dry-run # Apply the changes vendor/bin/rector process src --config=vendor/ray/aura-sql-module/rector-migrate.php ``` ```bash vendor/bin/rector process tests --config=vendor/ray/aura-sql-module/rector-migrate.php ``` -------------------------------- ### Customize Pagerfanta View Templates Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Bind a specific Pagerfanta template and configure view options like CSS classes and labels to customize pagination output. ```php install(new AuraSqlModule('mysql:host=localhost;dbname=myapp', 'user', 'pass')); $this->install(new AuraSqlPagerModule()); // Use Bootstrap 5 pagination template $this->bind(TemplateInterface::class)->to(TwitterBootstrap5Template::class); // Configure view options $this->bind('')->annotatedWith(PagerViewOption::class)->toInstance([ 'prev_message' => '« Previous', 'next_message' => 'Next »', 'css_container_class' => 'pagination justify-content-center', ]); } } ``` -------------------------------- ### Inject Named PDO Instance with NamedPdoEnvModule Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md Configure NamedPdoEnvModule to inject named PDO instances using environment variables for connection details. This is useful for managing different database connections dynamically. ```php class AppModule extends AbstractModule { protected function configure() { $this->install(new NamedPdoEnvModule('log_db', 'LOG_DSN', 'LOG_USERNAME', 'LOG_PASSWORD')); } } ``` -------------------------------- ### Paginate raw SQL queries with AuraSqlPagerFactory Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Use AuraSqlPagerFactoryInterface to paginate raw SQL strings. The factory returns a Page object that supports array access for specific pages and provides rendered HTML pagination. ```php 1]; if ($category) { $sql .= ' AND a.category = :category'; $params['category'] = $category; } $sql .= ' ORDER BY a.created_at DESC'; $pager = $this->pagerFactory->newInstance( $this->pdo, $sql, $params, 10, // Items per page '/?page={page}&category=' . $category // URI template ); // Access page by array index $pageResult = $pager[$page]; // Page object properties: // $pageResult->data - Array of results for current page // $pageResult->current - Current page number // $pageResult->total - Total number of pages // $pageResult->hasNext - Boolean: has next page // $pageResult->hasPrevious - Boolean: has previous page // $pageResult->maxPerPage - Items per page setting // (string) $pageResult - Rendered pagination HTML return $pageResult; } public function renderArticlePage(int $page): string { $pageResult = $this->getArticleList($page); $html = '
'; foreach ($pageResult as $article) { $html .= sprintf( '

%s

By %s

', htmlspecialchars($article['title']), htmlspecialchars($article['author_name']) ); } $html .= '
'; // Render pagination controls $html .= ''; return $html; } } ``` -------------------------------- ### Use ReadOnlyConnection for Read Operations Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt Use the `#[ReadOnlyConnection]` attribute to ensure methods like `findById` and `findByCategory` always use the read-only database connection, regardless of the HTTP request method. ```php pdo = $pdo; } #[ReadOnlyConnection] public function findById(int $id): ?array { // Always uses slave/read connection regardless of HTTP method return $this->pdo->fetchOne( 'SELECT * FROM products WHERE id = :id', ['id' => $id] ); } #[ReadOnlyConnection] public function findByCategory(string $category): array { // Complex read query always goes to slave return $this->pdo->fetchAll( 'SELECT p.*, c.name as category_name FROM products p JOIN categories c ON p.category_id = c.id WHERE c.slug = :category AND p.active = 1 ORDER BY p.created_at DESC', ['category' => $category] ); } } ``` -------------------------------- ### Remove Doctrine Annotations Dependency Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/ANNOTATION_TO_ATTRIBUTE.md Remove the abandoned doctrine/annotations package after completing the migration. ```bash composer remove doctrine/annotations ``` -------------------------------- ### Iterating Over Page Items Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/README.md The Page object is iterable, allowing direct access to the result set items. ```php foreach ($page as $item) { // ... } ``` -------------------------------- ### Complex Annotation Value Migration Source: https://github.com/ray-di/ray.aurasqlmodule/blob/1.x/ANNOTATION_TO_ATTRIBUTE.md Manual adjustment required for annotations containing complex array values. ```php /** * @Transactional({"pdo1", "pdo2"}) */ ``` ```php #[Transactional(["pdo1", "pdo2"])] ``` -------------------------------- ### Automatic Transaction Management with Transactional Attribute in PHP Source: https://context7.com/ray-di/ray.aurasqlmodule/llms.txt The #[Transactional] attribute automatically manages database transactions, ensuring rollback on exceptions. Use #[WriteConnection] to specify the connection for transactional operations. ```php pdo = $pdo; } #[WriteConnection, Transactional] public function placeOrder(int $userId, array $items): int { // All operations within this method are wrapped in a transaction // If any exception is thrown, the entire transaction is rolled back // Create order $this->pdo->perform( 'INSERT INTO orders (user_id, status, created_at) VALUES (:user_id, :status, NOW())', ['user_id' => $userId, 'status' => 'pending'] ); $orderId = (int) $this->pdo->lastInsertId(); // Add order items foreach ($items as $item) { $this->pdo->perform( 'INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (:order_id, :product_id, :quantity, :price)', [ 'order_id' => $orderId, 'product_id' => $item['product_id'], 'quantity' => $item['quantity'], 'price' => $item['price'] ] ); // Update inventory $this->pdo->perform( 'UPDATE products SET stock = stock - :quantity WHERE id = :id', ['quantity' => $item['quantity'], 'id' => $item['product_id']] ); } // Update order total $total = $this->pdo->fetchValue( 'SELECT SUM(quantity * price) FROM order_items WHERE order_id = :order_id', ['order_id' => $orderId] ); $this->pdo->perform( 'UPDATE orders SET total = :total WHERE id = :id', ['total' => $total, 'id' => $orderId] ); return $orderId; // Transaction commits automatically if no exception is thrown } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.