neutrino-labs/laravel-mongodb-sync

Hybrid MySQL-MongoDB synchronization package for Laravel microservices

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/neutrino-labs/laravel-mongodb-sync

dev-main 2026-02-19 19:00 UTC

This package is auto-updated.

Last update: 2026-02-19 19:02:14 UTC


README

PHP 8.2+ Laravel 11|12 MongoDB 5.x+ MySQL 8.0+ License

πŸš€ Hybrid MySQL-MongoDB synchronization for high-performance Laravel microservices

Query MongoDB with automatic MySQL fallback, relationship auto-detection, field selection, eager loading, and production-ready security.

πŸ“‹ Table of Contents

✨ Features

🎯 Core Features

  • βœ… Hybrid Queries: MongoDB-first with automatic MySQL fallback
  • βœ… Auto-Detection: Automatic Eloquent relationship detection via Reflection
  • βœ… Field Selection: Reduce payload by 50%+ with selective field loading
  • βœ… Eager Loading: Eliminate N+1 queries (6 queries β†’ 2 queries)
  • βœ… Type Safety: Full PHP 8.2+ type hints and strict types
  • βœ… Zero Config: Works out-of-the-box with sensible defaults

πŸ”’ Security (P1 Fixes)

  • βœ… NoSQL Injection Prevention: Operator whitelist validation
  • βœ… Input Sanitization: Auto-removal of dangerous characters ($, {}, etc.)
  • βœ… DoS Mitigation: Maximum 1000 records per query
  • βœ… Sensitive Data Protection: Auto-removal of passwords/tokens from API responses
  • βœ… Field Validation: Blocks MongoDB operators in field names

⚑ Performance (P2 Optimizations)

  • βœ… Configurable Cache: File, Redis, Database, Memcached support
  • βœ… Batch Loading: Eager loading reduces queries by 50-70%
  • βœ… Persistent Cache: Relationship metadata cached across requests
  • βœ… Memory Efficient: < 1MB memory usage per request
  • βœ… Query Optimization: Automatic index hints and projections

πŸ› οΈ Developer Experience

  • βœ… Fluent API: Chainable query builder
  • βœ… Facades: Laravel-style facades for clean code
  • βœ… Logging: Comprehensive error logging and debugging
  • βœ… PSR-4: Modern PHP standards

πŸ“¦ Requirements

Component Version
PHP ^8.2
Laravel ^11.0 | ^12.0
MongoDB ^5.0
MySQL ^8.0
mongodb/laravel-mongodb ^5.0

πŸš€ Installation

1. Install via Composer

composer require neutrino-labs/laravel-mongodb-sync

2. Publish Configuration

php artisan vendor:publish --tag=mongodb-sync-config

3. Configure Environment

# MongoDB Connection
MONGODB_CONNECTION=mongodb
MONGODB_HOST=127.0.0.1
MONGODB_PORT=27017
MONGODB_DATABASE=your_database
MONGODB_USERNAME=your_username
MONGODB_PASSWORD=your_password

# MySQL Fallback
MONGODB_MYSQL_FALLBACK=true

# Cache Strategy (file, redis, database, memcached)
MONGODB_CACHE_DRIVER=file

# Cache TTL (seconds)
MONGODB_RELATION_CACHE_TTL=3600

βš™οΈ Configuration

config/mongodb-sync.php

<?php

return [
    // MongoDB connection from config/database.php
    'connection' => env('MONGODB_CONNECTION', 'mongodb'),

    // Enable automatic MySQL fallback
    'mysql_fallback' => env('MONGODB_MYSQL_FALLBACK', true),

    // Cache driver: file, redis, database, memcached, array
    'cache_driver' => env('MONGODB_CACHE_DRIVER', 'file'),

    // Relationship cache TTL (seconds)
    'relation_cache_ttl' => env('MONGODB_RELATION_CACHE_TTL', 3600),

    // Auto-detect Eloquent relationships
    'auto_detect_relations' => env('MONGODB_AUTO_DETECT_RELATIONS', true),

    // Model to collection mapping
    'collection_map' => [
        \App\Models\User::class => 'users',
        \App\Models\Post::class => 'posts',
        // Add your models here
    ],

    // Field selection per relationship (reduces payload by 50%+)
    'relation_select' => [
        'posts' => ['id', 'title', 'status', 'created_at'],
        'user' => ['id', 'name', 'email'],
    ],
];

🎯 Quick Start

Basic Query

use Neutrino\MongoDBSync\Services\MongoDBQueryService;

$mongo = new MongoDBQueryService('users');
$users = $mongo->where('status', '=', 'active')->get();

With Relationships

use Neutrino\MongoDBSync\Services\HybridQueryService;
use App\Models\User;

$hybrid = new HybridQueryService();
$user = $hybrid->findWithRelations(
    collection: 'users',
    modelClass: User::class,
    field: 'id',
    value: 1
);

// Auto-loads: $user->posts, $user->profile, etc.

Search API

use Neutrino\MongoDBSync\Services\SearchService;
use Illuminate\Http\Request;

$searchService = new SearchService($hybridQuery);
return $searchService->search(
    collection: 'users',
    modelClass: User::class,
    request: $request,
    perPage: 15
);

πŸ“– Usage Guide

Basic Queries

Simple Queries

use Neutrino\MongoDBSync\Services\MongoDBQueryService;

$mongo = new MongoDBQueryService('users');

// Find by ID
$user = $mongo->find(1);

// Where clause
$users = $mongo->where('age', '>', 18)->get();

// Multiple conditions
$users = $mongo
    ->where('status', '=', 'active')
    ->where('age', '>=', 18)
    ->get();

// Select specific fields
$users = $mongo
    ->select(['name', 'email', 'created_at'])
    ->get();

// Limit and offset
$users = $mongo->limit(10)->offset(20)->get();

// Order by
$users = $mongo->orderBy('created_at', 'desc')->get();

Supported Operators

Operator Description Example
= Equal where('age', '=', 25)
!=, <> Not equal where('status', '!=', 'banned')
> Greater than where('age', '>', 18)
>= Greater or equal where('score', '>=', 100)
< Less than where('price', '<', 50)
<= Less or equal where('stock', '<=', 10)
like Pattern match where('name', 'like', 'John%')
in In array where('id', 'in', [1,2,3])
nin Not in array where('status', 'nin', ['banned'])
gt, gte, lt, lte MongoDB native where('age', 'gt', 18)

MySQL Fallback

use App\Models\User;

$mongo = new MongoDBQueryService('users');
$mongo->withMySQLFallback(User::class);

$user = $mongo->find(1);
// Tries MongoDB first, falls back to MySQL if not found

Relationships

Auto-Detection

use Neutrino\MongoDBSync\Services\HybridQueryService;
use App\Models\User;

$hybrid = new HybridQueryService();

// Automatically detects: posts(), profile(), comments(), etc.
$user = $hybrid->findWithRelations(
    collection: 'users',
    modelClass: User::class,
    field: 'id',
    value: 1
);

echo $user->name;
foreach ($user->posts as $post) {
    echo $post->title;
}

Load into Collection

$users = User::limit(10)->get();

$usersWithRelations = $hybrid->loadRelationsIntoCollection(
    records: $users,
    collection: 'users',
    modelClass: User::class
);

Field Selection

Reduce payload size by 50%+ by selecting only needed fields.

Global Configuration

// config/mongodb-sync.php
'relation_select' => [
    'posts' => ['id', 'title', 'status', 'created_at'],
    'user' => ['id', 'name', 'email', 'avatar'],
],

Per-Endpoint Override

class UserController extends Controller
{
    public function index()
    {
        return $this->searchService->search(
            collection: 'users',
            modelClass: User::class,
            request: $request,
            perPage: 15,
            relationSelect: [
                'posts' => ['id', 'title', 'status'], // Less fields for list
            ]
        );
    }

    public function show($id)
    {
        return $this->searchService->findById(
            collection: 'users',
            modelClass: User::class,
            id: $id,
            relationSelect: [
                'posts' => ['id', 'title', 'content', 'status', 'created_at'], // More fields for detail
            ]
        );
    }
}

Results

Before: 2,356 bytes (10 fields per post)
After:    992 bytes (4 fields per post)
Reduction: 57.9% πŸŽ‰

Search & Filters

Basic Search

use Neutrino\MongoDBSync\Services\SearchService;

$searchService = new SearchService($hybridQuery);

// GET /api/users?page=1&per_page=15
$response = $searchService->search(
    collection: 'users',
    modelClass: User::class,
    request: $request,
    perPage: 15
);

Advanced Filters

// GET /api/users?filters[0][field]=status&filters[0][operator]==&filters[0][value]=active
$request = new Request([
    'filters' => [
        ['field' => 'status', 'operator' => '=', 'value' => 'active'],
        ['field' => 'age', 'operator' => '>', 'value' => 18],
    ],
    'page' => 1,
    'per_page' => 15,
]);

$response = $searchService->search('users', User::class, $request);

Response Format

{
  "success": true,
  "data": {
    "items": [
      {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "posts": [
          {"id": 1, "title": "Post 1", "status": "published"}
        ]
      }
    ],
    "pagination": {
      "total": 100,
      "per_page": 15,
      "current_page": 1,
      "last_page": 7,
      "from": 1,
      "to": 15
    }
  }
}

Caching

Configuration

# File cache (default, no dependencies)
MONGODB_CACHE_DRIVER=file

# Redis cache (high performance)
MONGODB_CACHE_DRIVER=redis

# Database cache (persistent)
MONGODB_CACHE_DRIVER=database

Manual Cache Control

$hybrid = new HybridQueryService();

// Clear cache for specific model
$hybrid->clearRelationshipCache(User::class);

// Clear all relationship cache
$hybrid->clearRelationshipCache();

Cache Benefits

  • 1st call: 1.08ms (with Reflection)
  • 2nd call: 0.05ms (from cache)
  • Improvement: 95% faster πŸš€

πŸ“š API Reference

MongoDBQueryService

new MongoDBQueryService(string $collection)

// Query Building
->where(string $field, string $operator, mixed $value): self
->select(array $fields): self
->limit(int $limit): self
->offset(int $offset): self
->orderBy(string $field, string $direction = 'asc'): self

// Execution
->get(): array
->first(): ?object
->find(mixed $id): ?object
->count(): int

// MySQL Fallback
->withMySQLFallback(string $modelClass): self

HybridQueryService

new HybridQueryService()

// Find with relationships
->findWithRelations(
    string $collection,
    string $modelClass,
    string $field,
    mixed $value,
    array $relations = [],
    array $eloquentRelations = [],
    bool $withTrashed = false
): ?object

// Load relationships into collection
->loadRelationsIntoCollection(
    $records,
    string $collection,
    string $modelClass,
    array $relations = [],
    array $eloquentRelations = [],
    ?array $relationSelect = null
)

// Detect relationships
->detectRelationships(string $modelClass): array

// Cache control
->clearRelationshipCache(?string $modelClass = null): void

SearchService

new SearchService(HybridQueryService $hybridQuery)

// Search with pagination
->search(
    string $collection,
    ?string $modelClass,
    Request $request,
    int $perPage = 15,
    ?array $relationSelect = null
): JsonResponse

// Find by ID
->findById(
    string $collection,
    ?string $modelClass,
    int $id,
    bool $withRelations = true,
    ?array $relationSelect = null
): JsonResponse

// Find by field
->findBy(
    string $collection,
    ?string $modelClass,
    string $field,
    mixed $value,
    bool $withRelations = true
): JsonResponse

⚑ Performance

Benchmarks

Operation Before After Improvement
N+1 Queries 6 queries 2 queries 67% reduction
Payload Size 2,356 bytes 992 bytes 57% reduction
Cache Hit 1.08ms 0.05ms 95% faster
Memory Usage ~5MB <1MB 80% reduction

Best Practices

1. Use Field Selection

'relation_select' => [
    'posts' => ['id', 'title', 'status', 'created_at'],
],

2. Enable Caching

MONGODB_CACHE_DRIVER=redis # Use Redis in production
MONGODB_RELATION_CACHE_TTL=3600

3. Batch Operations

// βœ… Good: 2 queries with eager loading
$usersWithRelations = $hybrid->loadRelationsIntoCollection($users, 'users', User::class);

4. Index MongoDB Collections

// MongoDB Shell
db.users.createIndex({ "status": 1, "created_at": -1 })
db.posts.createIndex({ "user_id": 1 })

πŸ”’ Security

Built-in Protections

1. NoSQL Injection Prevention

// ❌ Blocked
$mongo->where('email', '$where', 'malicious code'); // InvalidArgumentException

// βœ… Safe
$mongo->where('email', '=', 'john@example.com');

2. Field Name Validation

// ❌ Blocked: field names can't start with $
$mongo->where('$ne', '=', 'value');

// βœ… Safe
$mongo->where('status', '=', 'active');

3. DoS Protection

// Automatically limited to 1000 records max
$mongo->limit(5000)->get(); // Returns max 1000

4. Sensitive Data Removal

// Automatically removes from API responses:
// - password, remember_token, api_token
// - access_token, secret, secret_key

5. Input Sanitization

// Auto-removes dangerous characters: $, { }
// Limits string length to 500 chars
// Limits arrays to 1000 items

Security Checklist

  • βœ… Operator whitelist (15 safe operators)
  • βœ… Field name validation (no $ prefix)
  • βœ… Array size limits (max 1000 items)
  • βœ… Result limits (max 1000 records)
  • βœ… Value sanitization
  • βœ… Sensitive field removal
  • βœ… Comprehensive logging

πŸ› Troubleshooting

Relationships Not Loading

// Clear cache
$hybrid->clearRelationshipCache(User::class);

// Check model mapping in config/mongodb-sync.php
'collection_map' => [
    \App\Models\Post::class => 'posts', // ← Add this
],

Cache Not Working

# Use 'file' if Redis not available
MONGODB_CACHE_DRIVER=file

# Clear cache
php artisan config:clear
php artisan cache:clear

N+1 Queries

// Use eager loading
$users = $hybrid->loadRelationsIntoCollection($users, 'users', User::class);

// Enable query logging
DB::enableQueryLog();
dd(DB::getQueryLog());

πŸ“ Changelog

[3.0.0] - 2026-01-30

Added

  • ✨ Configurable cache strategy
  • ✨ Eager loading (N+1 fix)
  • ✨ Field selection in relationships (50%+ payload reduction)
  • πŸ”’ NoSQL injection prevention
  • πŸ”’ Input sanitization
  • πŸ”’ DoS protection
  • ⚑ Persistent cache

Changed

  • πŸ”„ Refactored relationship loading
  • πŸ”„ Enhanced security validations

Fixed

  • πŸ› Cache driver compatibility
  • πŸ› N+1 query problem
  • πŸ› Memory leaks

πŸ“„ License

MIT License - Copyright Β© 2026 Neutrino SoluΓ§Γ΅es em Tecnologia

This package is open source software licensed under the MIT License. You are free to use, modify, and distribute it.

πŸ‘₯ Authors

Neutrino SoluΓ§Γ΅es em Tecnologia
🌐 https://neutrino.dev.br
πŸ“§ neutrino@neutrino.dev.br

Made with ❀️ by Neutrino Soluçáes em Tecnologia