### Complete Rowcast Configuration Example Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md This example demonstrates the complete setup for Rowcast, including creating a connection, initializing a DataMapper with custom converters, and performing a query. ```php use AsceticSoft\Rowcast\Connection; use AsceticSoft\Rowcast\DataMapper; use AsceticSoft\Rowcast\NameConverter\SnakeCaseToCamelCase; use AsceticSoft\Rowcast\TypeConverter\TypeConverterRegistry; // 1. Create connection $connection = Connection::create( dsn: 'mysql:host=localhost;dbname=myapp;charset=utf8mb4', username: 'root', password: 'secret', options: [ \PDO::ATTR_TIMEOUT => 5, ], nestTransactions: true, typeConverter: TypeConverterRegistry::defaults(), ); // 2. Create DataMapper $mapper = new DataMapper( connection: $connection, nameConverter: new SnakeCaseToCamelCase(), typeConverter: TypeConverterRegistry::defaults() ->add(new CustomMoneyConverter()), ); // 3. Use it $users = $mapper->findAll( UserDto::class, where: ['is_active' => 1], orderBy: ['created_at' => 'DESC'], limit: 10, ); foreach ($users as $user) { echo $user->email; } ``` -------------------------------- ### Development Setup Commands Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Commands for setting up the development environment, including installing dependencies, installing git hooks, running tests, and static analysis. ```bash composer install make install-hooks vendor/bin/phpunit vendor/bin/phpstan analyse ``` -------------------------------- ### Install Dependencies Source: https://github.com/ascetic-soft/rowcast/blob/master/AGENTS.md Use these commands to install project dependencies via Make or Composer. ```bash make install ``` ```bash composer install ``` -------------------------------- ### beginTransaction() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Starts a database transaction. Creates SAVEPOINTs for nested transactions. ```APIDOC ## beginTransaction() ### Description Starts a database transaction. When nested transactions are enabled, inner calls create SAVEPOINTs instead of starting a real transaction. ### Method `public function beginTransaction(): void` ### Example ```php $connection->beginTransaction(); try { $connection->executeStatement('INSERT INTO users ...', [...]); $connection->commit(); } catch (\Throwable $e) { $connection->rollBack(); } ``` ``` -------------------------------- ### Upsert Method Example Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Provides an example of how to use the `upsert` method for inserting or updating records based on conflict properties. ```APIDOC ## `upsert(...)` Method Example ### Description The `upsert` method inserts a record if it does not exist, or updates it if a conflict occurs based on the specified conflict properties. This is generally more efficient than `save` when database conflict handling is supported. ### Usage ```php $affected = $mapper->upsert('users', $dto, 'email'); ``` ``` -------------------------------- ### Type Conversion Examples Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Extractor.md Demonstrates how Extractor applies type conversion for various PHP types to database-safe formats. ```php class UserDto { public int $id; public string $email; public bool $isActive; public array $metadata; public \DateTimeInterface $createdAt; public UserStatus $status; // BackedEnum } $user = new UserDto(); $user->id = 1; $user->email = 'alice@example.com'; $user->isActive = true; $user->metadata = ['key' => 'value']; $user->createdAt = new \DateTime('2024-01-15 10:30:00'); $user->status = UserStatus::Active; $data = $extractor->extract($user); // Conversion applied by type: // id: 1 ← ScalarConverter(unchanged) // email: 'alice@example.com' ← ScalarConverter(unchanged) // isActive: 1 ← BoolConverter(true → 1) // metadata: '{"key":"value"}' ← JsonConverter([...] // createdAt: '2024-01-15T10:30:00+00:00' ← DateTimeConverter(DateTime → string) // status: 'active' ← EnumConverter(UserStatus::Active → 'active') ``` -------------------------------- ### Setup Connection and DataMapper Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/README.md Establishes a database connection using PDO and initializes the DataMapper. Ensure you have the correct database credentials and DSN. ```php use AsceticSoft\Rowcast\Connection; use AsceticSoft\Rowcast\DataMapper; $connection = Connection::create( dsn: 'mysql:host=localhost;dbname=app', username: 'root', password: 'secret', nestTransactions: true, ); $mapper = new DataMapper($connection); ``` -------------------------------- ### Connection Factory with Options Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md Example of using the `Connection::create` factory method with PDO options, nested transactions, and a type converter. ```php $connection = Connection::create( dsn: 'mysql:host=localhost;dbname=app;charset=utf8mb4', username: 'root', password: 'secret', options: [ \PDO::ATTR_TIMEOUT => 5, ], nestTransactions: true, typeConverter: TypeConverterRegistry::defaults(), ); ``` -------------------------------- ### Install Rowcast with Composer Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Install the Rowcast package using Composer. This command adds the package as a dependency to your project. ```bash composer require ascetic-soft/rowcast ``` -------------------------------- ### Start SELECT Query with Columns Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Begin a SELECT query by specifying the columns to retrieve. This method can be chained with other query building methods. ```php $qb->select('u.id', 'u.email', 'COUNT(o.id) as order_count') ->from('users', 'u'); ``` -------------------------------- ### insert() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Starts an INSERT query for a specified table. ```APIDOC ## insert() ### Description Starts an INSERT query. ### Method `insert(string $table): self` ### Parameters #### Path Parameters - **$table** (string) - Required - Table name. ### Request Example ```php $qb->insert('users') ->values(['email' => ':email', 'is_active' => ':is_active']) ->setParameter('email', 'alice@example.com') ->setParameter('is_active', 1); ``` ``` -------------------------------- ### User Search Example Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/where-operators.md Demonstrates a real-world scenario for user search using multiple conditions including null checks, equality, and nested OR logic for email or name matching. ```php $users = $mapper->findAll(UserDto::class, where: [ 'deleted_at' => null, // Not deleted 'is_active' => 1, // Active '$or' => [ ['email LIKE' => '%@example.com'], ['name LIKE' => 'John%'], ], ]); // SQL: WHERE deleted_at IS NULL AND is_active = 1 AND // ((email LIKE ... OR name LIKE ...)) ``` -------------------------------- ### Configuration Reference Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/INDEX.txt Details on constructor options, defaults, connection configuration (DSN, PDO options), type converter and name converter setup, and environment variable handling. ```APIDOC ## Configuration Reference ### Description Explains how to configure the Rowcast DataMapper Library, including constructor options, connection settings, and custom converter setups. ### Configuration Options - Constructor parameters with types and defaults - Connection configuration (DSN, PDO options) - Type converter setup patterns - Name converter setup patterns - Environment variable handling ### Documentation Details - Complete setup examples - Source file references (file path and line number) ``` -------------------------------- ### select() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Starts a SELECT query and specifies the columns to retrieve. This method can be chained with other QueryBuilder methods to build a complete query. ```APIDOC ## select() ### Description Starts a SELECT query, specifying columns to retrieve. ### Method `public function select(string ...$columns): self` ### Parameters #### Path Parameters * **...$columns** (`string`) - Required - Column expressions (e.g., `'u.id'`, `'u.email'`, `'COUNT(*)'`). ### Example ```php $qb->select('u.id', 'u.email', 'COUNT(o.id) as order_count') ->from('users', 'u'); ``` ``` -------------------------------- ### Quick Start: Insert and Find User Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Demonstrates basic usage of Rowcast for inserting a new user DTO and then finding it by email. Requires a UserDto class and a 'users' table. ```php use AsceticSoft\Rowcast\Connection; use AsceticSoft\Rowcast\DataMapper; class UserDto { public int $id; public string $email; public bool $isActive; } $connection = Connection::create('sqlite::memory:'); $mapper = new DataMapper($connection); $user = new UserDto(); $user->email = 'alice@example.com'; $user->isActive = true; $mapper->insert('users', $user); $found = $mapper->findOne(UserDto::class, ['email' => $user->email]); ``` -------------------------------- ### Start DELETE Query with Table and Alias Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Initiates a DELETE query for a specified table, optionally with an alias. Use where() to define the conditions for deletion. ```php $qb->delete('users') ->where('id = :id') ->setParameter('id', 1); ``` -------------------------------- ### Basic SELECT with Pagination Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Select specific columns from a table with pagination. Use setOffset to define the starting point and setLimit for the maximum number of rows. ```php $rows = $connection->createQueryBuilder() ->select('u.id', 'u.email') ->from('users', 'u') ->where('u.is_active = :active') ->orderBy('u.id', 'DESC') ->setOffset(20) ->setLimit(10) ->setParameter('active', 1) ->fetchAllAssociative(); ``` -------------------------------- ### Start UPSERT Query and Define Values Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Begins an UPSERT query (INSERT ... ON CONFLICT or ON DUPLICATE KEY UPDATE) and specifies the column-placeholder pairs for the insert operation. ```php $qb->upsert('users') ->values(['id' => ':id', 'email' => ':email']) ->onConflict('id') ->doUpdateSet(['email']); ``` -------------------------------- ### upsert() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Starts an UPSERT query (INSERT ... ON CONFLICT / ON DUPLICATE KEY UPDATE) for a specified table. ```APIDOC ## upsert() ### Description Starts an UPSERT query (INSERT ... ON CONFLICT / ON DUPLICATE KEY UPDATE). ### Method `upsert(string $table): self` ### Parameters #### Path Parameters - **$table** (string) - Required - Table name. ### Request Example ```php $qb->upsert('users') ->values(['id' => ':id', 'email' => ':email']) ->onConflict('id') ->doUpdateSet(['email']); ``` ``` -------------------------------- ### Get Database Connection Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/DataMapper.md Returns the underlying database connection object. ```php public function getConnection(): ConnectionInterface ``` ```php $connection = $mapper->getConnection(); $driver = $connection->getDriverName(); ``` -------------------------------- ### Connection Class API Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/INDEX.txt Documentation for the Connection class includes its constructor, factory method, query execution capabilities, transaction management, and utility methods. All public methods are documented with signatures and examples. ```APIDOC ## Connection Class ### Description Provides functionality for establishing and managing database connections, executing queries, and handling transactions. ### Methods - Constructor - Factory method - Query execution methods - Transaction management methods - Utility methods ### Documentation Details - Full method signatures with parameter tables - Return type documentation - Description of behavior and purpose - Complete working examples - Source file references (file path and line number) - Cross-references to related methods ``` -------------------------------- ### NameConverters Reference Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/INDEX.txt Documentation on name conversion between database and PHP, featuring the default SnakeCaseToCamelCase converter and examples of custom converter implementations. ```APIDOC ## Name Converters ### Description Explains how database column names are converted to PHP property names and vice versa, including the default converter and customization options. ### Converters - SnakeCaseToCamelCase (default) - Custom converter examples ### Documentation Details - Name conversion between database and PHP - Complete working examples - Source file references (file path and line number) ``` -------------------------------- ### update() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Starts an UPDATE query for a specified table, optionally with an alias. ```APIDOC ## update() ### Description Starts an UPDATE query. ### Method `update(string $table, ?string $alias = null): self` ### Parameters #### Path Parameters - **$table** (string) - Required - Table name. - **$alias** (string|null) - Optional - Table alias. ### Request Example ```php $qb->update('users') ->set('email', ':email') ->where('id = :id') ->setParameter('email', 'alice@example.com') ->setParameter('id', 1); ``` ``` -------------------------------- ### Start INSERT Query with Table Name Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Initiates an INSERT query by specifying the target table. Use with values() or setValue() to define columns and placeholders. ```php $qb->insert('users') ->values(['email' => ':email', 'is_active' => ':is_active']) ->setParameter('email', 'alice@example.com') ->setParameter('is_active', 1); ``` -------------------------------- ### Start UPDATE Query with Table and Alias Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Initiates an UPDATE query for a specified table, optionally with an alias. Use set() to define column updates and where() for conditions. ```php $qb->update('users') ->set('email', ':email') ->where('id = :id') ->setParameter('email', 'alice@example.com') ->setParameter('id', 1); ``` -------------------------------- ### PHP Usage with PrefixedConverter Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/NameConverters.md Demonstrates using a PrefixedConverter with DataMapper for automatic hydration and extraction. This example shows how to set a custom converter globally and perform find and update operations. ```php // Use custom converter globally for all operations $mapper = new DataMapper( $connection, nameConverter: new PrefixedConverter('usr_') ); // All auto-mappings use the custom converter $user = $mapper->findOne(UserDto::class, ['id' => 1]); $user->email = 'newemail@example.com'; $mapper->update('users', $user, ['id' => 1]); ``` -------------------------------- ### DateTimeConverter Configuration Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/TypeConverters.md Configures the DateTimeConverter with a custom date format. This example shows how to modify an existing registry or create a new one with a specific format. ```php // Custom format $converters = TypeConverterRegistry::defaults(); $converter = new DateTimeConverter(format: 'Y-m-d H:i:s'); // Remove default, add custom foreach ($converters->toDb(new \DateTime()) as $c) { if (!$c instanceof DateTimeConverter) { continue; } } // OR create fresh registry: $converters = new TypeConverterRegistry([ new ScalarConverter(), new BoolConverter(), new DateTimeConverter(format: 'Y-m-d H:i:s'), new JsonConverter(), new EnumConverter(), ]); $mapper = new DataMapper($connection, typeConverter: $converters); ``` -------------------------------- ### Connection::create() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md A static factory method to create a new Connection instance directly from DSN parameters. This method simplifies the setup process by handling default PDO options and optional parameters. ```APIDOC ## Static Factory Method: create() ### Description Creates a new Connection from DSN parameters, simplifying the setup process by applying default PDO options and handling optional connection details. ### Method `create` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **$dsn** (`string`) - Required - PDO data source name (e.g., `'mysql:host=localhost;dbname=app'`). - **$username** (`string|null`) - Optional - Database username. Defaults to `null`. - **$password** (`string|null`) - Optional - Database password. Defaults to `null`. - **$options** (`array`) - Optional - PDO driver options, merged with defaults. Defaults to `[]`. - **$nestTransactions** (`bool`) - Optional - Enable savepoint-based nested transactions. Defaults to `false`. - **$typeConverter** (`TypeConverterInterface|null`) - Optional - Custom type converter registry. Defaults to `null`. ### Returns `self` ### Request Example ```php $connection = Connection::create( dsn: 'mysql:host=localhost;dbname=app', username: 'root', password: 'secret', nestTransactions: true, ); ``` ``` -------------------------------- ### PHP Combining Explicit Mapping with DataMapper Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/NameConverters.md Illustrates how explicit `Mapping` bypasses name converters during operations. This example defines explicit column mappings for a UserDto. ```php use AsceticSoft\Rowcast\Mapping; $mapping = Mapping::explicit(UserDto::class, 'users') ->column('user_id', 'id') ->column('user_email', 'email') ->column('is_active', 'isActive'); // Explicit mapping ignores the name converter $user = $mapper->findOne($mapping, ['id' => 1]); ``` -------------------------------- ### Run All Tests Source: https://github.com/ascetic-soft/rowcast/blob/master/AGENTS.md Execute the full test suite using the Make command. ```bash make test ``` -------------------------------- ### delete() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Starts a DELETE query for a specified table, optionally with an alias. ```APIDOC ## delete() ### Description Starts a DELETE query. ### Method `delete(string $table, ?string $alias = null): self` ### Parameters #### Path Parameters - **$table** (string) - Required - Table name. - **$alias** (string|null) - Optional - Table alias. ### Request Example ```php $qb->delete('users') ->where('id = :id') ->setParameter('id', 1); ``` ``` -------------------------------- ### setOffset() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Sets the OFFSET clause for pagination, specifying the starting row for the query results. ```APIDOC ## setOffset() ### Description Sets the OFFSET clause (starting row for pagination). ### Method `setOffset(int $offset): self` ### Parameters #### Path Parameters - **$offset** (int) - Required - Number of rows to skip. ### Request Example ```php $qb->setLimit(10)->setOffset(20); // Get rows 20-30 ``` ``` -------------------------------- ### Get All Ignored Properties Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Mapping.md Retrieves a list of all DTO property names that are currently configured to be ignored. ```php public function getIgnoredProperties(): array ``` ```php $ignored = $mapping->getIgnoredProperties(); // ['internalNote', 'calculatedFields'] ``` -------------------------------- ### Run CI Checks Source: https://github.com/ascetic-soft/rowcast/blob/master/AGENTS.md Execute the standard local gate in CI order using the Make command. ```bash make ci ``` -------------------------------- ### MySQL Connection with Credentials Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md Configure a MySQL database connection using a DSN string, username, and password. ```php $connection = Connection::create( dsn: 'mysql:host=localhost;port=3306;dbname=myapp;charset=utf8mb4', username: 'root', password: 'secret', ); ``` -------------------------------- ### setValue() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Sets a single column value for an INSERT or UPDATE query. If the value starts with ':', it's treated as a parameter placeholder. ```APIDOC ## setValue() ### Description Sets a single column value. If value is a string starting with `:`, it's treated as a parameter placeholder. ### Method `setValue(string $column, mixed $value): self` ### Parameters #### Path Parameters - **$column** (string) - Required - Column name. - **$value** (mixed) - Required - Value or placeholder (`:name`). ### Request Example ```php $qb->insert('users') ->setValue('email', 'alice@example.com') ->setValue('is_active', ':is_active') ->setParameter('is_active', 1); ``` ``` -------------------------------- ### PostgreSQL Connection with Credentials Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md Configure a PostgreSQL database connection using a DSN string, username, and password. ```php $connection = Connection::create( dsn: 'pgsql:host=localhost;port=5432;dbname=myapp', username: 'postgres', password: 'secret', ); ``` -------------------------------- ### Set Pagination Offset in QueryBuilder Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Use setOffset() to specify the starting row for paginated results. This method is chained after setLimit(). ```php $qb->setLimit(10)->setOffset(20); // Get rows 20-30 ``` -------------------------------- ### Partial Updates with Extractor Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Extractor.md Demonstrates how to use the Extractor to prepare data for partial updates by only including modified properties. ```php $user = new UserDto(); $user->id = 1; $user->email = 'newemail@example.com'; // Only property to update // Other properties not set $data = $extractor->extract($user); // Data contains only: id and email $mapper->update('users', $user, ['id' => 1]); // Updates only email column (and maybe id for logging) ``` -------------------------------- ### Get Driver Name Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Returns the name of the underlying PDO driver, such as 'mysql' or 'pgsql'. Useful for implementing driver-specific logic. ```php $driver = $connection->getDriverName(); if ($driver === 'pgsql') { // PostgreSQL-specific logic } ``` -------------------------------- ### Connection Constructor Signature Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md Shows the constructor signature for the Connection class and the factory method `create`. ```php public function __construct( \PDO $pdo, bool $nestTransactions = false, ?TypeConverterInterface $typeConverter = null, ) // Or use the factory: public static function create( string $dsn, ?string $username = null, ?string $password = null, array $options = [], bool $nestTransactions = false, ?TypeConverterInterface $typeConverter = null, ) ``` -------------------------------- ### DataMapper Basic Usage Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/README.md Demonstrates basic read and write operations using the DataMapper. Requires a Connection instance and a DTO class. ```php $connection = Connection::create('mysql:host=localhost;dbname=app', 'root', 'secret'); $mapper = new DataMapper($connection); // Read $user = $mapper->findOne(UserDto::class, ['email' => 'alice@example.com']); // Write $user->email = 'newemail@example.com'; $mapper->update('users', $user, ['id' => $user->id]); ``` -------------------------------- ### Get Transaction Nesting Level Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Returns the current transaction nesting depth. Returns 0 if nested transactions are disabled. ```php public function getTransactionNestingLevel(): int { // ... implementation details ... } ``` -------------------------------- ### Get Last Insert ID Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Retrieves the ID of the last inserted row. An optional sequence name can be provided for PostgreSQL. ```php public function lastInsertId(?string $name = null): string|false { // ... implementation details ... } $connection->executeStatement('INSERT INTO users (email) VALUES (?)', ['alice@example.com']); $id = $connection->lastInsertId(); ``` -------------------------------- ### Run PHPStan Analysis Source: https://github.com/ascetic-soft/rowcast/blob/master/AGENTS.md Perform static analysis using PHPStan via the Make command. ```bash make phpstan ``` -------------------------------- ### Create Connection using Static Factory Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Create a Connection instance using the static create() method, providing DSN and authentication details. This method also allows configuration of nested transactions and custom type converters. ```php $connection = Connection::create( dsn: 'mysql:host=localhost;dbname=app', username: 'root', password: 'secret', nestTransactions: true, ); ``` -------------------------------- ### Basic CRUD Operations with DataMapper Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Demonstrates the basic insert, findOne, update, and delete operations using the DataMapper. ```php $dto = new UserDto(); $dto->email = 'alice@example.com'; $dto->isActive = true; $mapper->insert('users', $dto); $one = $mapper->findOne(UserDto::class, ['email' => $dto->email]); $one->isActive = false; $mapper->update('users', $one, ['id' => $one->id]); $mapper->delete('users', ['id' => $one->id]); ``` -------------------------------- ### Run Code Style Check Source: https://github.com/ascetic-soft/rowcast/blob/master/AGENTS.md Check code style compliance using the Make command. ```bash make cs-check ``` -------------------------------- ### Get PDO Instance Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Retrieves the underlying PDO instance for direct access. Allows for advanced manipulation or inspection of the PDO connection. ```php $pdo = $connection->getPdo(); $attributes = $pdo->getAvailableDrivers(); ``` -------------------------------- ### Get Column Name for Property Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Mapping.md Retrieves the database column name associated with a DTO property. Returns null if no mapping exists. ```php public function getColumnForProperty(string $propertyName): ?string ``` ```php $column = $mapping->getColumnForProperty('isActive'); // Returns 'is_active' (or null if not mapped) ``` -------------------------------- ### Create Query Builder Instance Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Use createQueryBuilder to obtain a QueryBuilder instance associated with the current connection. This allows for fluent construction of SQL queries. ```php $rows = $connection->createQueryBuilder() ->select('*') ->from('users') ->where(['is_active' => 1]) ->fetchAllAssociative(); ``` -------------------------------- ### Get Type Converter Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Returns the type converter registry used by this connection. This registry handles data type conversions for database operations. ```php public function getTypeConverter(): TypeConverterInterface { // ... implementation details ... } ``` -------------------------------- ### CRUD Operations with DataMapper Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/README.md Demonstrates basic Create, Read, Update, and Delete operations using the DataMapper. Assumes a UserDto class and a 'users' table exist. ```php // Create $user = new UserDto(); $user->email = 'alice@example.com'; $user->isActive = true; $mapper->insert('users', $user); // Read $user = $mapper->findOne(UserDto::class, ['email' => $user->email]); // Update $user->isActive = false; $mapper->update('users', $user, ['id' => $user->id]); // Delete $mapper->delete('users', ['id' => $user->id]); ``` -------------------------------- ### Get Associated Connection Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Use `getConnection` to retrieve the `ConnectionInterface` object that the `QueryBuilder` instance is currently using. This allows access to connection-specific details. ```php public function getConnection(): ConnectionInterface ``` ```php $driver = $qb->getConnection()->getDriverName(); ``` -------------------------------- ### UuidConverter Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/TypeConverters.md An example of a custom type converter that handles `Ramsey\Uuid\UuidInterface` and `Ramsey\Uuid\Uuid` types. It converts string representations of UUIDs to UuidInterface objects and vice versa. ```APIDOC ## UuidConverter (Custom Example) ### Description Handles `Ramsey\Uuid\UuidInterface` and `Ramsey\Uuid\Uuid` types. Converts string UUIDs to UuidInterface objects and UuidInterface objects to strings. ### Constructor This converter does not have a constructor that requires parameters. ### Methods #### `supports(string $phpType): bool` Checks if the converter supports the given PHP type (`UuidInterface` or `Uuid`). #### `toPhp(mixed $value, string $phpType): UuidInterface` Converts a string or `UuidInterface` value to a `UuidInterface` object. #### `toDb(mixed $value): string` Converts a `UuidInterface` object to its string representation. ``` -------------------------------- ### Connection Constructor Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Initializes a new Connection instance. It wraps an existing PDO object and provides convenience methods for database operations. It automatically configures PDO to throw exceptions on errors. ```APIDOC ## Constructor Connection ### Description Initializes a new Connection instance, wrapping an existing PDO object and enabling convenient database operations. It ensures PDO is configured to throw exceptions on errors. ### Method `__construct` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **$pdo** (`\PDO`) - Required - Underlying PDO instance. PDO is configured to throw exceptions on errors. - **$nestTransactions** (`bool`) - Optional - When true, nested `beginTransaction()`/`commit()`/`rollBack()` calls use SQL SAVEPOINTs instead of failing. Defaults to `false`. - **$typeConverter** (`TypeConverterInterface|null`) - Optional - Custom type converter registry. Defaults to `TypeConverterRegistry::defaults()` if not provided. ### Request Example ```php use AsceticSoft\Rowcast\Connection; $pdo = new \PDO('sqlite::memory:'); $connection = new Connection($pdo, nestTransactions: true); ``` ``` -------------------------------- ### Get Compiled SQL String Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Use `getSQL` to retrieve the generated SQL query string without executing it. This is helpful for debugging or logging the query. ```php public function getSQL(): string ``` ```php $sql = $qb->select('*')->from('users')->getSQL(); echo $sql; // SELECT * FROM users ``` -------------------------------- ### Get PDO Driver Name Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md The dialect is automatically selected based on the PDO driver name. You can retrieve the driver name using the getDriverName() method. ```php $connection->getDriverName(); // 'mysql', 'pgsql', 'sqlite', etc. ``` -------------------------------- ### Instantiate Hydrator with Default Converters Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Hydrator.md Instantiate the Hydrator with default type and name converters. This is typically done by the DataMapper, but can be done manually if needed. ```php use AsceticSoft\Rowcast\Hydrator; use AsceticSoft\Rowcast\TypeConverter\TypeConverterRegistry; use AsceticSoft\Rowcast\NameConverter\SnakeCaseToCamelCase; $hydrator = new Hydrator( typeConverter: TypeConverterRegistry::defaults(), nameConverter: new SnakeCaseToCamelCase(), ); $object = $hydrator->hydrate(UserDto::class, $row, $mapping); ``` -------------------------------- ### Configuring a Custom Name Converter Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md Implement NameConverterInterface to define custom logic for converting between column names and property names. This example uses a prefixed converter. ```php use AsceticSoft\Rowcast\NameConverter\NameConverterInterface; class PrefixedConverter implements NameConverterInterface { public function __construct(private string $prefix = 'usr_') {} public function toPropertyName(string $columnName): string { return lcfirst(str_replace($this->prefix, '', $columnName)); } public function toColumnName(string $propertyName): string { return $this->prefix . $propertyName; } } $mapper = new DataMapper( $connection, nameConverter: new PrefixedConverter('usr_'), ); ``` -------------------------------- ### Execute Raw SQL Queries Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Provides methods for executing raw SQL statements and fetching results. Supports parameterized queries for security and fetching single rows or all rows associatively. ```php $stmt = $connection->executeQuery('SELECT * FROM users WHERE id = ?', [1]); $affected = $connection->executeStatement('UPDATE users SET email = ? WHERE id = ?', ['a@x.com', 1]); $rows = $connection->fetchAllAssociative('SELECT * FROM users'); $row = $connection->fetchAssociative('SELECT * FROM users WHERE id = ?', [1]); $count = $connection->fetchOne('SELECT COUNT(*) FROM users'); ``` -------------------------------- ### Implementing a Custom UuidConverter Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/TypeConverters.md Provides an example of implementing a custom TypeConverterInterface for handling UUIDs. This converter ensures that UUID strings are correctly converted to and from UuidInterface objects. ```php use AsceticSoft\Rowcast\TypeConverter\TypeConverterInterface; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; final class UuidConverter implements TypeConverterInterface { public function supports(string $phpType): bool { return $phpType === UuidInterface::class || $phpType === Uuid::class; } public function toPhp(mixed $value, string $phpType): UuidInterface { if ($value instanceof UuidInterface) { return $value; } return Uuid::fromString((string) $value); } public function toDb(mixed $value): string { if (!$value instanceof UuidInterface) { throw new \InvalidArgumentException('UuidConverter expects UuidInterface.'); } return $value->toString(); } } ``` -------------------------------- ### Connection Class Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/types.md PDO wrapper with transaction and query execution helpers. ```APIDOC ## Class: Connection ### Description PDO wrapper with transaction and query execution helpers. ### Constructor - `__construct( raverse $pdo, bool $nestTransactions = false, ?TypeConverterInterface $typeConverter = null)` ### Static Methods - `create(string $dsn, ?string $username = null, ?string $password = null, array $options = [], bool $nestTransactions = false, ?TypeConverterInterface $typeConverter = null): self ``` -------------------------------- ### Specify FROM Table Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Define the main table from which to select data. An alias can be provided for brevity. ```php $qb->select('*') ->from('users', 'u'); ``` -------------------------------- ### Fetch Rows as Iterable Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Use `toIterable` to execute a query and get an iterable that yields rows one at a time. This is memory-efficient for fetching large result sets. ```php public function toIterable(): iterable ``` ```php foreach ($qb->select('*')->from('users')->toIterable() as $row) { processRow($row); } ``` -------------------------------- ### UPSERT Statement Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Create an UPSERT statement to insert new rows or update existing ones based on a conflict constraint. This example targets conflicts on the 'email' column. ```php $sql = $connection->createQueryBuilder() ->upsert('users') ->values(['email' => ':email', 'name' => ':name']) ->onConflict('email') ->doUpdateSet(['name']) ->getSQL(); ``` -------------------------------- ### Configuring Connection String Directly Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md Rowcast does not use environment variables for configuration. Provide the DSN, username, and password directly when creating the connection. ```php $dsn = getenv('DATABASE_URL') ?: 'sqlite::memory:'; $connection = Connection::create($dsn); ``` ```php $connection = Connection::create( dsn: getenv('DATABASE_URL'), username: getenv('DATABASE_USER'), password: getenv('DATABASE_PASS'), ); ``` -------------------------------- ### from() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Specifies the main table from which to retrieve data for the query. An alias can be provided for the table name. ```APIDOC ## from() ### Description Specifies the main table for the query. ### Method `public function from(string $table, ?string $alias = null): self` ### Parameters #### Path Parameters * **$table** (`string`) - Required - Table name. * **$alias** (`string|null`) - Optional - Table alias (defaults to table name). ### Example ```php $qb->select('*') ->from('users', 'u'); ``` ``` -------------------------------- ### Handling Missing Required Properties Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Hydrator.md Explains that missing required properties without default values result in uninitialized properties, not errors, during hydration. ```php class UserDto { public int $id; // Required, no default } $row = []; // No 'id' column $user = $hydrator->hydrate(UserDto::class, $row); // $user->id is uninitialized (no error thrown) ``` -------------------------------- ### Registering a Custom Type Converter Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md Implement TypeConverterInterface to define custom logic for converting between PHP types and database values. This example shows a MoneyConverter for a Money::class type. ```php use AsceticSoft\Rowcast\TypeConverter\TypeConverterInterface; use AsceticSoft\Rowcast\TypeConverter\TypeConverterRegistry; class MoneyConverter implements TypeConverterInterface { public function supports(string $phpType): bool { return $phpType === Money::class; } public function toPhp(mixed $value, string $phpType): Money { [$currency, $amount] = explode(':', (string) $value); return Money::of($amount, new Currency($currency)); } public function toDb(mixed $value): string { return $value->getCurrency()->getCode() . ':' . $value->getAmount(); } } $converters = TypeConverterRegistry::defaults() ->add(new MoneyConverter()); $mapper = new DataMapper($connection, typeConverter: $converters); ``` -------------------------------- ### fetchOne() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Executes the query and returns the first column of the first row. ```APIDOC ## fetchOne() ### Description Executes the query and returns the first column of the first row. ### Method `fetchOne(): mixed` ### Request Example ```php $count = $qb->select('COUNT(*)') ->from('users') ->fetchOne(); ``` ``` -------------------------------- ### SQLite File Connection Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/configuration.md Configure a SQLite database connection to a file using a DSN string. ```php $connection = Connection::create('sqlite:/path/to/db.sqlite3'); $connection = Connection::create('sqlite:db.sqlite3'); ``` -------------------------------- ### Set Single Column Value for INSERT Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/QueryBuilder.md Use setValue() to set a specific column's value in an INSERT query. If the value starts with ':', it's treated as a parameter placeholder. ```php $qb->insert('users') ->setValue('email', 'alice@example.com') ->setValue('is_active', ':is_active') ->setParameter('is_active', 1); ``` -------------------------------- ### getPdo() Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Connection.md Returns the underlying PDO instance for direct access when needed. ```APIDOC ## getPdo() ### Description Returns the underlying PDO instance for direct access when needed. ### Method `public function getPdo(): \PDO` ### Response Returns `\PDO` ### Example ```php $pdo = $connection->getPdo(); $attributes = $pdo->getAvailableDrivers(); ``` ``` -------------------------------- ### Instantiate DataMapper Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/DataMapper.md Create a new DataMapper instance with a database connection. Optional parameters allow for custom name converters, type converters, hydrators, extractors, and target resolvers. ```php use AsceticSoft\Rowcast\DataMapper; use AsceticSoft\Rowcast\Connection; $connection = Connection::create('sqlite::memory:'); $mapper = new DataMapper($connection); ``` -------------------------------- ### Custom Type Converter Implementation Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Provides an example of implementing a custom type converter for handling specific PHP types like Uuid. This involves implementing the TypeConverterInterface and adding it to the TypeConverterRegistry. ```php use AsceticSoft\Rowcast\DataMapper; use AsceticSoft\Rowcast\TypeConverter\TypeConverterInterface; use AsceticSoft\Rowcast\TypeConverter\TypeConverterRegistry; final class UuidConverter implements TypeConverterInterface { public function supports(string $phpType): { return $phpType === Uuid::class; } public function toPhp(mixed $value, string $phpType): { return new Uuid((string) $value); } public function toDb(mixed $value): { return (string) $value; } } $converters = TypeConverterRegistry::defaults()->add(new UuidConverter()); $mapper = new DataMapper($connection, typeConverter: $converters); ``` -------------------------------- ### Query Builder Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Introduces the Query Builder available via `Connection::createQueryBuilder()` for fluent SQL construction. ```APIDOC ## Query Builder ### Description The `Connection::createQueryBuilder()` method provides a fluent interface for constructing SQL queries programmatically. This allows for building complex SQL statements in a more readable and maintainable way compared to raw SQL strings. ### Usage ```php // Example of obtaining the QueryBuilder instance $queryBuilder = $connection->createQueryBuilder(); // Further methods would be called on $queryBuilder to construct SQL ``` ``` -------------------------------- ### Run Single Test File Source: https://github.com/ascetic-soft/rowcast/blob/master/AGENTS.md Execute tests from a specific file using PHPUnit. ```bash vendor/bin/phpunit tests/V2DataMapperTest.php ``` -------------------------------- ### Custom Name Converter Implementation Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Demonstrates how to implement a custom name converter to map between database column names and PHP property names. This involves implementing the NameConverterInterface. ```php use AsceticSoft\Rowcast\DataMapper; use AsceticSoft\Rowcast\NameConverter\NameConverterInterface; final class PrefixedConverter implements NameConverterInterface { public function toPropertyName(string $columnName): { return lcfirst(str_replace('usr_', '', $columnName)); } public function toColumnName(string $propertyName): { return 'usr_' . $propertyName; } } $mapper = new DataMapper($connection, nameConverter: new PrefixedConverter()); ``` -------------------------------- ### Extracting Data for Manual SQL Queries Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Extractor.md Use the `extract` method to get data in a format suitable for manual SQL queries. The resulting array can be directly used with query builders or iterated for parameter binding. ```php $user = new UserDto(); $user->email = 'alice@example.com'; $user->isActive = true; $data = $mapper->extract('users', $user); // ['email' => 'alice@example.com', 'is_active' => 1] // Use in raw SQL: $qb = $mapper->getConnection()->createQueryBuilder(); $qb->insert('users')->values($data); // Or iterate: foreach ($data as $column => $value) { $qb->setParameter($column, $value); } ``` -------------------------------- ### Auto Mapping with DTO Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/README.md Illustrates automatic mapping between DTO properties and database columns using default naming conventions. Ensure DTO properties match expected database columns. ```php class UserDto { public int $id; public string $email; public bool $isActive; // Maps to is_active column public \DateTimeInterface $createdAt; // Maps to created_at column } $user = $mapper->findOne(UserDto::class, ['email' => 'alice@example.com']); ``` -------------------------------- ### Get Property Name for Column Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Mapping.md Retrieve the DTO property name associated with a given database column name. Returns `null` if the column is not explicitly mapped or if auto-discovery does not resolve it. This is helpful for dynamic column processing or validation. ```php $property = $mapping->getPropertyForColumn('is_active'); // Returns 'isActive' (or null if not mapped) ``` -------------------------------- ### Using SnakeCaseToCamelCase with DataMapper Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/NameConverters.md Demonstrates how to use the default SnakeCaseToCamelCase converter with the DataMapper for fetching DTO objects from database results. ```php use AsceticSoft\Rowcast\DataMapper; class UserDto { public int $id; public string $email; public bool $isActive; public \DateTimeInterface $createdAt; } // Use default SnakeCaseToCamelCase converter $mapper = new DataMapper($connection); // findOne queries: SELECT * FROM users WHERE email = ? $user = $mapper->findOne(UserDto::class, ['email' => 'alice@example.com']); // Database row: ['id' => 1, 'email' => '...', 'is_active' => 1, 'created_at' => '...'] // Automatically converts to UserDto with: // $user->id, $user->email, $user->isActive, $user->createdAt ``` -------------------------------- ### Get Explicit Column Mappings Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Mapping.md Retrieve an array of explicit column mappings, where keys are database column names and values are DTO property names. This method is useful for inspecting the current mapping configuration, especially after using `column()` to define specific mappings. ```php $columns = $mapping->getColumns(); // ['id' => 'id', 'usr_email' => 'email'] ``` -------------------------------- ### Save Method Explanation Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Explains the functionality of the `save` method, which performs a read-before-write operation to insert or update records. ```APIDOC ## `save(...)` Method ### Description The `save(...)` method checks for the existence of a row by its identity columns and then performs either an insert or an update operation. It is a convenience API that uses a read-before-write flow and does not require a database conflict constraint. It may utilize two SQL statements for a single save operation. ### Usage ```php $mapper->save('users', $dto, 'id'); ``` ### Notes - This method is a convenience API. - It performs a read-before-write flow. - It does not require a database conflict constraint. - It may use two SQL statements for one logical save operation. - For write-path efficiency when database and schema support conflict handling, prefer `upsert(...)`. ``` -------------------------------- ### Instantiate Extractor Source: https://github.com/ascetic-soft/rowcast/blob/master/_autodocs/api-reference/Extractor.md Instantiate the Extractor with name and type converters. Typically managed by DataMapper, not direct instantiation. ```php use AsceticSoft\Rowcast\Extractor; use AsceticSoft\Rowcast\TypeConverter\TypeConverterRegistry; use AsceticSoft\Rowcast\NameConverter\SnakeCaseToCamelCase; $extractor = new Extractor( nameConverter: new SnakeCaseToCamelCase(), typeConverter: TypeConverterRegistry::defaults(), ); $data = $extractor->extract($userDto, $mapping); ``` -------------------------------- ### DataMapper Main Methods Source: https://github.com/ascetic-soft/rowcast/blob/master/README.md Core methods for interacting with the DataMapper, including insert, update, delete, find, and save operations. ```APIDOC ## DataMapper API ### Methods - `insert(string|Mapping $target, object $dto): void` - Inserts a single data object into the specified target. - `batchInsert(string|Mapping $target, array $dtos, ?int $maxBindParameters = null): void` - Inserts multiple data objects in batches into the specified target. - `update(string|Mapping $target, object $dto, array $where): int` - Updates records in the specified target based on the provided DTO and where clause. - `batchUpdate(string|Mapping $target, array $dtos, array $identityProperties, ?int $maxBindParameters = null): void` - Updates multiple records in batches based on identity properties. - `delete(string|Mapping $target, array $where): int` - Deletes records from the specified target based on the where clause. - `findAll(string|Mapping $target, array $where = [], array $orderBy = [], ?int $limit = null, ?int $offset = null): array` - Retrieves all records matching the criteria from the specified target. - `iterateAll(string|Mapping $target, array $where = [], array $orderBy = [], ?int $limit = null, ?int $offset = null): iterable` - Returns an iterable for all records matching the criteria. - `findOne(string|Mapping $target, array $where = []): ?object` - Retrieves a single record matching the criteria from the specified target. - `save(string|Mapping $target, object $dto, string ...$identityProperties): void` - Saves a data object by performing an insert or update based on identity properties. - `upsert(string|Mapping $target, object $dto, string ...$conflictProperties): int` - Inserts a record or updates it if a conflict occurs based on conflict properties. - `batchUpsert(string|Mapping $target, array $dtos, array $conflictProperties, ?int $maxBindParameters = null): void` - Performs batch upsert operations. - `hydrate(...)` - Hydrates data objects. - `hydrateAll(...)` - Hydrates multiple data objects. - `extract(...)` - Extracts data objects. ```