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

Sdk Laravel Package

complex-heart/sdk

A Laravel/PHP SDK for the Complex Heart platform, offering a clean, expressive client with typed responses, authentication helpers, and framework-ready service providers and facades for quick integration.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require complex-heart/sdk

There is no service provider, no config file, and no publishable assets — it's a plain library of abstract classes, traits, and small value objects. Installation is genuinely just the require. Verify the PHP constraint in vendor/complex-heart/sdk/composer.json before you commit (it targets modern PHP; if your app is still on 8.0 you'll get a resolver conflict).

Where to look first. Don't hunt for docs — src/ is the documentation. Spend ten minutes on:

find vendor/complex-heart/sdk/src -type d
grep -rn "abstract class\|^trait " vendor/complex-heart/sdk/src

You'll find two functional areas worth caring about:

  • Domain/Model — base ValueObject, Entity, AggregateRoot and the traits that compose them (identity, attributes, timestamps, domain events).
  • Domain/Criteria — filter/order/pagination objects for describing queries without leaking SQL into your domain.

First use case: a value object, not a rewrite. The highest-value, lowest-risk entry point is wrapping one primitive in a ValueObject. Pick something with real invariants — an email, a money amount, a tenant ID — and leave the rest of your models alone for now:

use ComplexHeart\Domain\Model\ValueObject;

final class Email extends ValueObject
{
    public function __construct(private readonly string $value)
    {
        if (filter_var($value, FILTER_VALIDATE_EMAIL) === false) {
            throw new InvalidArgumentException("Invalid email: {$value}");
        }
    }

    public function __toString(): string
    {
        return $this->value;
    }
}

Check which methods the base class actually leaves abstract (grep -n "abstract" src/Domain/Model/ValueObject.php) — depending on the release it may expect __toString() only, or a broader serialization contract. Getting this right on day one saves an afternoon.

Once a VO feels natural, move up one level: an AggregateRoot with a couple of recorded events, then a Criteria object passed to a repository. That's the package's whole arc, and it's a day's work.

Implementation Patterns

Aggregate + events, flushed at the application boundary. The pattern the SDK nudges you toward is: mutate the aggregate, record events on it, and release them exactly once — in the command handler, after the transaction commits:

final class Order extends AggregateRoot
{
    public static function place(OrderId $id, CustomerId $customer): self
    {
        $order = new self($id);
        $order->record(new OrderWasPlaced($id));

        return $order;
    }
}

// command handler
DB::transaction(function () use ($command) {
    $order = Order::place(OrderId::generate(), $command->customerId);
    $this->orders->save($order);
    $events = $order->releaseEvents();
});

foreach ($events as $event) {
    event($event); // or dispatch explicitly
}

The important discipline: the domain records, the application dispatches. Don't let an Eloquent observer silently dispatch what the aggregate already recorded.

Criteria as the query contract. Criteria is the piece with the most day-to-day payoff and the most DIY work. It gives you a domain-safe way to express "filter by status, sort by created_at desc, page 2" that your controllers can build and your repositories can consume:

// controller / FormRequest
$criteria = Criteria::fromArray($request->validated());

// domain port
interface UserRepository
{
    public function matching(Criteria $criteria): UserCollection;
}

Translate Criteria in the infrastructure layer, never in the domain. The package deliberately knows nothing about Eloquent/Doctrine. You write the adapter once:

final class EloquentUserRepository implements UserRepository
{
    private const ALLOWED_FILTERS = ['status', 'role', 'created_at'];

    public function matching(Criteria $criteria): UserCollection
    {
        $query = UserModel::query();

        foreach ($criteria->filters() as $filter) {
            abort_unless(in_array($filter->field(), self::ALLOWED_FILTERS, true), 422);
            $query->where($filter->field(), $filter->operator(), $filter->value());
        }

        return new UserCollection($query->paginate($criteria->limit())->items());
    }
}

Keep aggregate persistence hand-mapped. Resist extending your Eloquent models from Entity. The idiomatic shape is a thin UserModel for the database and a separate User aggregate plus a mapper. If that feels heavy for a small CRUD screen, that's a signal the SDK is the wrong tool for that screen — and that's fine, mixed codebases are normal.

Testing. The base classes make assertions cheap: self::assertTrue($a->equals($b)) for value objects, and self::assertContains(OrderWasPlaced::class, array_map('get_class', $order->releaseEvents())) for behaviour. Write those two test styles early; they're what keeps DDD refactors honest.

Gotchas and Tips

  • Bus factor and adoption are genuine risks. One star, zero dependents, a single maintainer. Pin the version exactly in composer.json, commit composer.lock, and be prepared to vendor-fork. The code is small and MIT, so forking is realistic — but do the read-through before you build ten aggregates on top of it.
  • No framework integration means no safety net. No auto-discovery, no config, no artisan commands, no documented upgrade path. Read CHANGELOG/commit history between releases; small libraries like this do ship breaking changes in minor versions.
  • releaseEvents() is easy to forget, and forgetting it fails silently. Your aggregate records happily, the tests on the aggregate pass, and production just never sends the email. Add a unit test per aggregate asserting the expected event list.
  • Double-dispatch is the mirror-image bug. If an Eloquent observer or a model event also fires, you'll notify twice. Keep event emission in exactly one place.
  • Criteria will hand you arbitrary field names. Always whitelist allowed filters and operators in the adapter — an unvalidated field from fromArray($request->all()) is a SQL injection surface, or at minimum a 500.
  • Value object equality deserves a test. Whether the base equals() compares by property identity, reflection, or serialized form matters once a VO holds an array or another object. Two Money(100, 'USD') instances should compare equal; confirm the base does that rather than assuming.
  • Trait collisions. Composing several model traits can produce duplicate method names. Fix with insteadof / as, and check the use blocks in the SDK's own abstract classes to see the intended combination.
  • Serialization is not a stable contract. Don't rely on toArray()/jsonSerialize() shapes for persistence across versions; write your own mapper if you need durable representation.
  • Extension points are the traits and the abstract bases. Override releaseEvents(), extend Criteria, compose the individual traits rather than inheriting the kitchen-sink aggregate. Everything else is designed to be left alone.
  • Debugging tip: in tinker, dump($aggregate->releaseEvents()) before wiring dispatchers — it immediately shows whether the domain or the plumbing is broken.
  • Run PHPStan at level 6+. The package is typed but not generic-annotated, so static analysis is what catches the argument-shape mistakes you'd otherwise find at runtime.
  • Honest scoping: if you only need typed primitives and query objects, complex-heart/sdk is a reasonable low-ceremony choice. If you need full event sourcing, sagas, or projections, this is not that package — look at Broadway or a plain custom implementation instead of stretching these abstractions.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib