Weave 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

Think Orm Laravel Package

webman/think-orm

Integrates ThinkORM with webman, bringing ThinkPHP's ORM, models, and query builder to webman applications. Configure database connections and use familiar ThinkORM APIs for rapid PHP development.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and first run

composer require webman/think-orm

The package is a thin bootstrap around topthink/think-orm (the standalone ORM extracted from ThinkPHP). It does not ship its own query builder — everything you know from ThinkPHP 6/8 models applies.

  1. Config first. After install, look for config/plugin/webman/think-orm/thinkorm.php. If it is not there, copy the default from vendor/webman/think-orm/config/plugin/webman/think-orm/thinkorm.php. The shape is the standard think-orm config:
return [
    'default'     => 'mysql',
    'connections' => [
        'mysql' => [
            'type'            => 'mysql',
            'hostname'        => '127.0.0.1',
            'database'        => 'webman',
            'username'        => 'root',
            'password'        => '',
            'hostport'        => 3306,
            'params'          => [],
            'charset'         => 'utf8mb4',
            'prefix'          => '',
            'deploy'          => 0,
            'rw_separate'     => false,
            'break_reconnect' => true,
            'debug'           => false,
        ],
    ],
];
  1. Where to look. vendor/webman/think-orm/src/ (the bootstrap that calls Db::setConfig() at worker start), then the config above, then vendor/topthink/think-orm and the ThinkPHP database docs — the framework glue is tiny, the ORM is the real subject.

  2. First use case. A controller that reads a table:

use think\facade\Db;

public function index()
{
    $users = Db::name('user')          // prefix + "user"
        ->where('status', 1)
        ->order('id', 'desc')
        ->limit(20)
        ->select();

    return json($users);
}

Db::name('user') applies the configured prefix; Db::table('user') uses the literal name. When a "table not found" error surprises you, that distinction is almost always the cause.

  1. First model. Create app/model/User.php:
namespace app\model;

use think\Model;

class User extends Model
{
    protected $name = 'user';
    protected $autoWriteTimestamp = true;
}

Then User::find(1), User::where('status', 1)->select(), $user->save().

Implementation Patterns

Query builder vs. models. Use Db::name() for reports, dashboards, bulk updates and anything that does not map to an aggregate; use models where you want relations, events, soft deletes and casts. Mixing them against the same tables is fine — they resolve to the same connection.

Relations and eager loading. Standard think-orm: hasMany, belongsTo, belongsToMany, plus ->with(['posts', 'posts.comments']) to avoid N+1. In a worker-based runtime the N+1 cost is paid inside the same process that is serving other queued requests, so eager loading matters more than it does under PHP-FPM.

Transactions. Prefer the closure form so a thrown exception rolls back automatically:

Db::transaction(function () use ($data) {
    $order = Order::create($data);
    OrderItem::insertAll($items);
});

Nested calls inside the closure use savepoints. Avoid holding a transaction open across an HTTP call or a slow external service — see the worker-blocking note below.

Pagination for APIs. paginate() returns a think-orm paginator whose render() emits Bootstrap HTML. For JSON endpoints do not render it:

$page = Db::name('user')->paginate(['list_rows' => 20, 'page' => $request->get('page', 1)]);
return json([
    'data'  => $page->items(),
    'total' => $page->total(),
    'page'  => $page->currentPage(),
]);

Multiple connections and read/write split. Declare extra connections ('pg' => [...]) and select with Db::connect('pg'), or pin a model with protected $connection = 'pg';. For primary/replica, set 'deploy' => 1 and 'rw_separate' => true on the connection and list hostname as an array — think-orm routes SELECT to slaves and writes to master. Be aware that read-after-write inside a transaction still needs Db::master(true).

Logging queries into webman's logger. 'debug' => true routes SQL through think-orm's own log facade, which is not necessarily wired to webman's support\Log. The reliable pattern is a listener registered in a bootstrap file:

use think\facade\Db;
use support\Log;

Db::listen(function ($sql, $time = null) {
    Log::debug($sql, ['time' => $time]);
});

For a one-off, use ->fetchSql(true) to get the SQL string instead of executing it — the fastest debugging tool in this ORM.

Custom processes and CLI. Db is configured during worker bootstrap, so custom processes in config/process.php and webman/console commands can use it normally. Do not touch Db from code that runs before bootstraps (e.g., very early in start.php).

Testing. Point a sqlite connection at :memory: (or a temp file) and call Db::setConfig() in your test bootstrap; the same models and query builder work unchanged for most CRUD.

Migrations. This package adds none. Use robmorgan/phinx, a raw SQL folder applied by a console command, or generate schema from your models — do not expect think-migration to work outside a ThinkPHP application skeleton.

Gotchas and Tips

There is no connection pool. Each worker process holds one PDO handle per connection name, reused across every request that worker serves. Three consequences:

  • Stale connections. MySQL/proxies drop idle connections; you get "MySQL server has gone away". Keep 'break_reconnect' => true and, for critical paths, catch PDOException and retry once. Keeping MySQL's wait_timeout above your traffic gaps helps too.
  • State leakage. Session variables, SET statements, SET NAMES, or a forgotten SELECT ... FOR UPDATE persist into the next request on that worker. Always reset what you change.
  • Leaked transactions are fatal. If an exception escapes without a rollback, the connection stays inside a transaction and every later request on that worker inherits it — including its locks. Use Db::transaction() or try/catch with an explicit Db::rollback(), and never let a request end mid-transaction.

A slow query blocks the whole worker. Webman is synchronous per process: a 2-second query stalls every request queued on that worker. Raise the count on the HTTP worker in config/process.php, add indexes, and avoid long transactions that span external calls.

Do not mix with webman/database (Illuminate). Two ORMs means two connection configs, two Db facades (support\Db vs think\facade\Db) and two sets of conventions in one codebase. If you must co-exist during a migration, keep config/database.php and config/plugin/webman/think-orm/thinkorm.php in sync and be explicit about which facade each file imports.

Config is namespaced and cached at boot. The plugin reads plugin.webman.think-orm.thinkorm, not a top-level config/thinkorm.php. Config and model code changes require php webman reload (or a restart) — workers do not pick them up live. If you prefer a top-level file, load it yourself: Db::setConfig(config('thinkorm')) in a bootstrap.

Timestamps and timezone drift. $autoWriteTimestamp = true writes integer create_time/update_time. If your columns are DATETIME, set protected $autoWriteTimestamp = 'datetime'; and configure datetime_format. Webman sets PHP's default timezone from config/app.php; make sure MySQL's time_zone matches, otherwise NOW() defaults and PHP-generated timestamps disagree by hours.

Turn debug off in production. With debug => true every query is stringified and passed to a logger — measurable overhead on hot paths, and the log facade may be unbound, producing confusing errors rather than useful logs. Use Db::listen() + support\Log instead.

Low adoption, mature core. The package has single-digit stars and no dependents, so community answers specific to webman + think-orm are scarce. Search the ThinkPHP database docs instead — the API is identical. Pin topthink/think-orm explicitly in composer.json; it is a transitive dependency here and major versions have shifted behaviour in the Db facade and query builder.

Extension points. type accepts a class name, not just a driver string — point it at your own class extending think\db\connector\Mysql to add tracing, custom PDO attributes, or a different builder. Extend the query builder with think\db\Query::extend('whereActive', fn(...) => ...) if you want domain-specific scopes available everywhere.

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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route