Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pdo Laravel Package

atlas/pdo

Decorates any PDO instance with a Connection that adds perform() (query + bind in one call), handy fetch*/yield* helpers, and query logging with backtraces. Includes a ConnectionLocator to manage named default/read/write connections.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require atlas/pdo
    
  2. Basic PDO Wrapping: Create a PDO instance and wrap it with Atlas\Pdo\Connection:

    use Atlas\Pdo\Connection;
    
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $connection = Connection::new($pdo);
    
  3. First Use Case: Execute a query with automatic binding using perform():

    $result = $connection->perform('SELECT * FROM users WHERE id = ?', [1]);
    $user = $result->fetch();
    
  4. Query Logging (optional): Enable logging to debug queries:

    $connection->logQueries();
    $queries = $connection->getQueries(); // Array of logged queries with backtraces
    

Implementation Patterns

Usage Patterns

  1. Replacing Raw PDO: Replace repetitive PDO operations with perform() and fetch*() methods:

    // Raw PDO
    $stmt = $pdo->prepare('SELECT * FROM posts WHERE user_id = ?');
    $stmt->execute([$userId]);
    $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    // Atlas.Pdo
    $posts = $connection->perform('SELECT * FROM posts WHERE user_id = ?', [$userId])
                        ->fetchAll();
    
  2. Streaming Results: Use yield* methods for large datasets to avoid memory issues:

    foreach ($connection->yieldFetchAll('SELECT * FROM large_table') as $row) {
        // Process row
    }
    
  3. Connection Management: Use ConnectionLocator for read/write separation:

    $locator = new ConnectionLocator();
    $locator->register('default', $defaultPdo);
    $locator->registerRead('slave', $slavePdo);
    $locator->registerWrite('master', $masterPdo);
    
    $readConnection = $locator->getRead();  // Uses first registered read connection
    $writeConnection = $locator->getWrite(); // Uses first registered write connection
    
  4. Dependency Injection: Bind ConnectionLocator to Laravel’s service container:

    $this->app->singleton(ConnectionLocator::class, function () {
        $locator = new ConnectionLocator();
        $locator->register('default', new PDO(...));
        return $locator;
    });
    

    Inject into services:

    public function __construct(private ConnectionLocator $locator) {}
    
  5. Query Logging Integration: Log queries to Laravel’s logging channel:

    $connection->setQueryLogger(function ($query, $backtrace) {
        \Log::debug("Query: {$query}", ['backtrace' => $backtrace]);
    });
    

Workflows

  1. Repository Pattern: Encapsulate database logic in repositories:

    class UserRepository {
        public function __construct(private Connection $connection) {}
    
        public function findById(int $id): ?array {
            return $this->connection->perform('SELECT * FROM users WHERE id = ?', [$id])
                                   ->fetch();
        }
    }
    
  2. Multi-Database Transactions: Use separate connections for read/write operations:

    $locator = app(ConnectionLocator::class);
    $writeConnection = $locator->getWrite();
    $writeConnection->beginTransaction();
    
    try {
        $writeConnection->perform('INSERT INTO orders (...) VALUES (...)');
        $writeConnection->commit();
    } catch (\Exception $e) {
        $writeConnection->rollBack();
        throw $e;
    }
    
  3. Testing: Mock Connection or ConnectionLocator in tests:

    $mockConnection = $this->createMock(Connection::class);
    $mockConnection->method('perform')->willReturn($this->createMock(Result::class));
    $this->app->instance(ConnectionLocator::class, $locator);
    

Integration Tips

  1. Laravel Facade: Create a facade to mimic Laravel’s DB syntax:

    class AtlasFacade extends Facade {
        protected static function getFacadeAccessor() {
            return ConnectionLocator::class;
        }
    }
    

    Usage:

    Atlas::getRead()->perform('SELECT ...');
    
  2. Query Builder Compatibility: Use perform() with Laravel’s query builder SQL:

    $sql = DB::table('users')->where('active', true)->toSql();
    $bindings = DB::getQuery()->getBindings();
    $users = $connection->perform($sql, $bindings)->fetchAll();
    
  3. Error Handling: Wrap perform() calls in try-catch blocks:

    try {
        $result = $connection->perform('UPDATE users SET ...');
    } catch (\PDOException $e) {
        \Log::error("Database error: " . $e->getMessage());
        throw new \RuntimeException("Failed to update user", 0, $e);
    }
    
  4. Persistent Connections: Enable persistent connections for performance:

    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass', [
        PDO::ATTR_PERSISTENT => true,
    ]);
    $connection = Connection::new($pdo);
    

Gotchas and Tips

Pitfalls

  1. Boolean Binding: PDO treats booleans as 0 or 1 strings, which can cause issues with strict typing. Atlas.Pdo handles this automatically, but be aware of edge cases:

    // Works correctly with Atlas.Pdo
    $connection->perform('SELECT * FROM users WHERE active = ?', [true]);
    
  2. Connection Lifecycle: Connections are opened eagerly, which may impact connection pooling in high-concurrency apps. Use ConnectionLocator to manage lifecycles:

    // Avoid creating connections manually in loops
    foreach ($users as $user) {
        $connection = new Connection(new PDO(...)); // Bad: Creates many connections
    }
    
  3. Query Logging Overhead: Logging queries adds overhead. Disable in production if not needed:

    $connection->logQueries(false);
    
  4. Backtrace Depth: Backtraces may not always point to the exact line where perform() was called, especially in minified or obfuscated code.

  5. Type Safety: The package uses PHP 8.5 features like #[ReturnTypeWillChange]. Ensure your Laravel app supports PHP 8.5+.

Debugging

  1. Query Logging: Enable logging to diagnose slow queries:

    $connection->logQueries();
    $queries = $connection->getQueries();
    foreach ($queries as $query) {
        \Log::debug("Query: {$query['query']}", ['backtrace' => $query['backtrace']]);
    }
    
  2. Result Inspection: Use fetch() methods to debug result shapes:

    $result = $connection->perform('SELECT * FROM users');
    $firstRow = $result->fetch(); // Returns first row as associative array
    $allRows = $result->fetchAll(); // Returns all rows
    
  3. Connection Issues: Check if the underlying PDO instance is valid:

    if (!$connection->isValid()) {
        throw new \RuntimeException("Database connection failed");
    }
    
  4. Transaction Debugging: Verify transactions are committed/rolled back:

    $connection->beginTransaction();
    try {
        $connection->perform('UPDATE accounts SET balance = balance - ? WHERE id = ?', [100, 1]);
        $connection->commit();
    } catch (\Exception $e) {
        $connection->rollBack();
        throw $e;
    }
    

Tips

  1. Custom Fetch Methods: Extend Connection to add domain-specific fetch methods:

    class UserConnection extends Connection {
        public function fetchUser(int $id): ?array {
            return $this->perform('SELECT * FROM users WHERE id = ?', [$id])
                        ->fetch();
        }
    }
    
  2. Query Builder Integration: Combine with Laravel’s query builder for dynamic SQL:

    $query = DB::table('users')->where('active', true);
    $sql = $query->toSql();
    $bindings = $query->getBindings();
    $users = $connection->perform($sql, $bindings)->fetchAll();
    
  3. Performance Tuning: Use yield* for large datasets to reduce memory usage:

    foreach ($connection->yieldFetchAll('SELECT * FROM large_table') as $row) {
        // Process row without loading all data into memory
    }
    
  4. Configuration: Centralize connection configurations in Laravel’s config/database.php:

    'connections' => [
        'atlas' => [
            'pdo'
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky