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

Yadm Laravel Package

formapro/yadm

formapro/yadm is a PHP client for the Yandex.Direct advertising API. It offers an object-oriented wrapper for campaigns, ad groups, ads, keywords, and reports, handling authentication, request building, and response parsing so PHP applications can automate Yandex.Direct account management.

Deep Wiki
Context7

Getting Started

Install the package together with the driver and library it sits on:

composer require formapro/yadm mongodb/mongodb

You need ext-mongodb and a running MongoDB. Yadm does not ship a bundle, config tree, or service provider — you wire it yourself, which is the first thing to notice.

Bootstrap a DocumentManager from a MongoDB\Database and a collection→class map:

use Formapro\Yadm\DocumentManager;

$client = new MongoDB\Client(getenv('MONGODB_URI'));
$db     = $client->selectDatabase('app');

$dm = DocumentManager::create($db, [
    'users'    => App\Document\User::class,
    'invoices' => App\Document\Invoice::class,
]);

$users = $dm->getStorage(App\Document\User::class);

Where to look first, in order:

  1. Formapro\Yadm\DocumentManager — how the map is built and how storages are resolved.
  2. Formapro\Yadm\Storage — the entire CRUD surface (insert, update, delete, find, findOne).
  3. Formapro\Yadm\Hydrator — the reflection-compiled hydrator; this is where the speed comes from and where all mapping quirks live.
  4. Formapro\Yadm\CollectionFactory — use it directly if you want a Storage without the manager.

Your first end-to-end use case should be deliberately boring: a plain PHP class, insert it, read it back.

class User
{
    public $_id;
    public string $name;
    public ?\DateTimeInterface $createdAt = null;
}

$user = new User();
$user->name = 'Ada';
$users->insert($user);          // _id is written back onto the object

$found = $users->findOne(['_id' => $user->_id]);

No base class, no annotations, no proxies. That is the whole selling point: fast hydration into objects you already own.

Implementation Patterns

Treat Storage as the low-level primitive, not the API. Wrap each storage in a repository so query details stay out of controllers:

final class UserRepository
{
    public function __construct(private Formapro\Yadm\Storage $storage) {}

    public function findActive(): iterable
    {
        return $this->storage->find(
            ['active' => true],
            ['sort' => ['createdAt' => -1], 'limit' => 100]
        );
    }
}

Configure casts on the hydrator. BSON does not round-trip to PHP types for free. \MongoDB\BSON\UTCDateTime stays a BSON object unless you cast it to \DateTimeImmutable; embedded documents arrive as arrays unless you register a nested hydrator. Centralize this per document class (via a HydratorFactory or a small factory method) so every storage for that class behaves identically.

Use projections for read paths. The hydrator only sets what it receives, so a lightweight read model plus ['projection' => ['name' => 1]] is the cheapest way to shrink payloads on list screens. Keep a separate, fuller class for edit screens.

Compose specifications for reusable filters. Formapro\Yadm\Specification lets you express "published in the last 30 days" once and reuse it across repositories instead of copy-pasting filter arrays.

Use the raw collection for anything analytical. $storage->getCollection() hands you the underlying MongoDB\Collection, which is what you want for aggregate(), bulkWrite(), distinct(), and index management. Yadm is a mapper; don't fight it for reporting queries.

Manage indexes out of band. Yadm does not create indexes for you. Put createIndex calls in an idempotent console command or migration so a fresh environment gets the same indexes as production.

Use change streams for side effects. Formapro\Yadm\ChangeStream\Consumer is useful for outbox-style patterns (send email, update search index) without putting that logic on the write path. It requires a replica set.

In a framework, hand-wire it. Register the DocumentManager as a singleton service (built from the client's database plus the map) and register each Storage as a factory. Do not let repositories construct their own clients.

Gotchas and Tips

  • The package is effectively frozen. Last release was May 2019. Expect friction with PHP 8.1+ and with mongodb/mongodb 1.15+. Pin versions deliberately and budget time to vendor a patch rather than assuming upstream fixes.
  • No unit of work, no identity map, no dirty tracking. Hydrated objects are detached. Mutating $user->name does nothing until you call $storage->update($user). This is the biggest conceptual shift for anyone coming from Doctrine.
  • _id appears only after a successful insert. Code that reads $model->_id before insert() returns gets null. Capture the return/assigned id immediately.
  • Typed properties and missing fields are a trap. If a document written by an older service lacks a key, hydration leaves the property uninitialized and the first read throws Error: must not be accessed before initialization. Default everything to null or provide initializers.
  • Hydration bypasses constructors and setters. Reflection writes properties directly, so validation and invariants in __construct or setName() are silently skipped. Validate explicitly after hydration, or use plain DTOs and map to domain objects yourself.
  • Dates are UTC. Cast BSON dates to DateTimeImmutable and convert at the edges; do not store local times.
  • References are not lazy-loaded and there is no relation mapping. You get whatever ids are in the document. Batch-fetch related documents with a single $in query to avoid N+1.
  • Nested objects need explicit configuration. Without a nested hydrator you get associative arrays, which will blow up on typed properties expecting an object.
  • Debug by comparing raw and hydrated forms. Dump $storage->getCollection()->findOne($filter)->getArrayCopy() next to the hydrated object; the diff tells you instantly whether the problem is a missing cast or bad data. Enable command monitoring on the MongoDB\Client when queries misbehave.
  • Watch BSON numeric types. Ints, floats, and Decimal128 come back as different PHP types; a field that is sometimes int and sometimes float will break strict typed properties.
  • Extension points are thin but sufficient. Subclass Hydrator (or supply a factory) for bespoke casting, and supply your own CollectionFactory to control read preference and write concern — both are worth using instead of patching internals.
  • Testing requires a real MongoDB. There is no in-memory implementation. Use a Docker service or mongodb-memory-server in CI and reset collections between tests.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console