DEV Community

Cover image for StormQueries – A Query Builder with ORM Superpowers
Michal Czerski
Michal Czerski

Posted on

StormQueries – A Query Builder with ORM Superpowers

StormQueries – A Query Builder with ORM Superpowers

Over the years working in web development, I’ve tried countless tools for working with databases.
Most of the time, they fell into one of two categories:

  • Query Builders (QB) – lightweight, fast, no schema configuration required. Great for quick queries. The downside? Once you need hierarchical data (1 invoice → many items), you’re on your own.
  • ORMs (Object-Relational Mappers) – powerful for handling relationships, but heavy and often require models, annotations, or schema definitions.

And I kept thinking: why isn’t there something in between?
So I built StormQueries 🚀


Why StormQueries?

StormQueries combines the simplicity of a Query Builder with the power of an ORM.
Here’s what it gives you out of the box:

  • ✅ Build dynamic queries depending on runtime parameters.
  • Reusable query structures.
  • Hierarchical data fetching (one-to-many).
  • ✅ Map results to your own objects without modifying models.
  • Zero schema configuration – just connect to the database.
  • ✅ Support for subqueries.
  • Intuitive syntax.

One example:
With StormQueries, you can run an incremental update (field = field + 1) — something that’s oddly tricky or impossible in many other tools.


Quick examples

Basic CRUD:

$product = $queries->find('products', ['id' => 5]);

$id = $queries->insert('products', [
    'name' => 'Golden watch',
    'price' => 465
]);

$queries->update('products', ['id' => $id], ['name' => 'Renamed product']);

$queries->delete('products', ['id' => $id]);
Enter fullscreen mode Exit fullscreen mode

Dynamic filtering:

$query = $queries
    ->select('products')
    ->join('product_photos', ['product_photos.product_id' => 'products.id'])
    ->where('is_in_sale', true);

if ($criteria->hasCategory()) {
    $query->where('category_id', $criteria->getCategoryId());
}

$products = $query->findAll();
Enter fullscreen mode Exit fullscreen mode

Subqueries:

$queries
    ->select(SubQuery::create($queries->select('products'), 'p'))
    ->where('p.product_id', 7)
    ->find();
Enter fullscreen mode Exit fullscreen mode

ORM-style relationships (without schema config):

$customer = $queries
    ->select('customers c', Map::select([
        'customer_id',
        'customer_name'
    ], Customer::class, classId: 'customer_id'))
    ->leftJoin('orders o', 'o.customer_id = c.customer_id', Map::many("orders", [
        'order_id'
    ], Order::class, classId: 'order_id'))
    ->where('c.customer_id', 90)
    ->find();

foreach ($customer->orders as $order) {
    echo $customer->customer_name . " - " . $order->id;
}
Enter fullscreen mode Exit fullscreen mode

How is it different?

  • Doctrine / Eloquent (PHP) – require schema definitions and models. StormQueries works right after connecting.
  • SQLAlchemy, Hibernate – powerful but heavy, with lots of boilerplate. StormQueries is lightweight and fast.
  • Pure Query Builders – flexible, but no hierarchical mapping. StormQueries bridges the gap.

Try it out

🔗 GitHub: storm-php-queries
📖 Docs & examples: stormmoredev.github.io/storm-php-queries


Final thoughts

StormQueries was born out of real-world needs: I wanted the simplicity of Query Builders with the convenience of ORMs, without their usual trade-offs.

If this sounds useful, check it out, give it a ⭐ on GitHub, or drop me some feedback.
Issues, discussions, and pull requests are more than welcome 🙌

This is just the beginning — I’m planning more features inspired by real application use cases. Stay tuned!


Top comments (0)