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

Crm Bundle Laravel Package

libre-informatique/crm-bundle

Symfony CRM bundle from Libre Informatique that adds ready-made customer relationship entities—contacts, organisations and linked address books—on top of the Blast core bundle. Doctrine-backed, extensible and easy to integrate into existing Symfony apps.

View on GitHub
Deep Wiki
Context7

Getting Started

First, calibrate expectations: this is a Symfony bundle, not a Laravel package. There is no ServiceProvider, no extra.laravel block, no facade, no publishable config. composer require libre-informatique/crm-bundle into a Laravel app will install a package nothing boots. Your day-to-day leverage is therefore one of two things:

  1. Domain harvest (default, recommended). Read the bundle for its CRM schema — contacts, companies, activity/notes/calls — and reimplement it in Eloquent. This is where 90% of the practical value sits for a Laravel team.
  2. Standalone service (only if you must reuse existing records). Stand the bundle up as its own Symfony app and talk to it over HTTP/queue. Heavier operationally, honest about the boundary.

Where to look first

In the repo, in this order:

Path Why it matters
LICENSE Reads as NOASSERTION. This is the gate. Read it before writing a line of code.
composer.json Pins for symfony/*, doctrine/orm, PHP version, and the sibling libre-informatique/* deps.
src/Entity/ (or Model/) The actual CRM schema. This is the asset you are harvesting.
src/Repository/ Query semantics: what "active contact", "open opportunity", "recent activity" mean.
DependencyInjection/Configuration.php What is configurable (relations, toggles) — tells you which schema choices were meant to be swapped.
Resources/config/doctrine/ or entity annotations Mapping details: join tables, naming strategy, cascade rules.

First use case in a Laravel app

Pick a thin vertical: contacts belonging to companies, with an activity feed of notes and calls. That single slice exercises the party/relationship modeling, the polymorphic activity association, and the address/communication-channel fields — the three things that are annoying to get right from scratch.

Minimal steps:

  1. Clear the license (or confirm internal-only use).
  2. Read the three entities for contact, company/organization, and interaction. Write down the relationships and enum vocabularies (statuses, channels, stages) before touching code.
  3. Translate to Laravel migrations and Eloquent models (see the next section for the mapping).
  4. Build CRUD with Filament or Nova — do not hand-roll forms.
  5. Write feature tests against the behavior you extracted in step 2, not against what you assumed the Symfony code does.

If legal is unresolved, stop at step 1 and use the bundle purely as a reading exercise.

Implementation Patterns

Pattern 1: Domain harvest → Eloquent (default)

Map the Symfony/Doctrine entities to Laravel models. Typical shape:

Symfony entity Laravel model Notes
Contact Contact Has address/phone/email fields or embedded value objects — flatten to columns or a JSON column.
Company / Organisation Company Contact belongsTo Company; decide nullable vs. required on day one.
Activity / Interaction (call, email, note) Activity Polymorphic subject() pointing at Contact and Company; type enum cast.
Embedded Address columns or json cast Doctrine embeddables do not translate; pick one representation and stay consistent.

Migrations — translate the semantics, not the DDL:

Schema::create('companies', function (Blueprint $t) {
    $t->id();
    $t->string('name')->index();
    $t->string('website')->nullable();
    $t->string('phone')->nullable();
    $t->json('address')->nullable();       // flattened embeddable
    $t->timestamps();
    $t->softDeletes();                     // explicit choice, not inherited
});

Schema::create('contacts', function (Blueprint $t) {
    $t->id();
    $t->foreignId('company_id')->nullable()->constrained()->nullOnDelete();
    $t->string('first_name');
    $t->string('last_name');
    $t->string('email')->nullable()->index();
    $t->string('phone')->nullable();
    $t->json('address')->nullable();
    $t->timestamps();
    $t->softDeletes();
});

Schema::create('activities', function (Blueprint $t) {
    $t->id();
    $t->morphs('subject');                 // contact or company
    $t->string('type')->index();           // call | email | note
    $t->text('body')->nullable();
    $t->timestamp('occurred_at')->nullable();
    $t->timestamps();
});

Models with enum casts and a strict morph map:

class Activity extends Model
{
    protected $guarded = [];

    protected $casts = [
        'type' => ActivityType::class,
        'occurred_at' => 'datetime',
    ];

    public function subject(): MorphTo
    {
        return $this->morphTo();
    }
}

// AppServiceProvider::boot()
Relation::enforceMorphMap([
    'contact' => Contact::class,
    'company' => Company::class,
]);

Isolate it in a domain namespace (app/Domain/Crm/...) with a small service layer — CreateActivity, TransferContactToCompany, etc. When (not if) you later swap to a SaaS CRM or a vetted package, the change is a namespace-level swap rather than a grep across controllers.

Pattern 2: Standalone Symfony service + Laravel adapter

Only when a hard requirement forces reuse of existing CRM records:

  1. Deploy the bundle as its own Symfony app in its own container.
  2. Define an OpenAPI or message contract (contact CRUD, activity append, list-by-company).
  3. Laravel adapter behind an interface — CrmClient — with timeouts, a circuit breaker, and idempotency keys on writes.
  4. Read-only cutover first; write path last.
interface CrmClient
{
    public function upsertContact(ContactData $data): ContactId;
    public function listActivities(ContactId $id): Collection;
}

Keep the adapter thin; put the retry/circuit-breaker in middleware so no caller has to know.

Pattern 3: Admin surface

Filament resources map cleanly onto a harvested model. Use Select::make('company_id')->relationship(...), Repeater for the activity log, and a MorphToSelect for the activity subject. This is the fastest way to get a usable CRM UI without touching Symfony Forms or Twig.

Day-to-day workflow

  • Schema questions: read the Symfony entity, ask "does this invariant matter to us?" — the original's answer is an input, not a mandate.
  • Behavior questions: read the repository class for the query it encodes, then re-express as an Eloquent scope.
  • Enum vocabularies: copy stages/statuses/channel types verbatim into a PHP enum. They are cheap and are where most of the domain thinking is captured.
  • Tests: write a contract test per extracted behavior, so the reimplementation is pinned to observed semantics, not assumptions.

Gotchas and Tips

The license is the real blocker. NOASSERTION means GitHub could not classify LICENSE. Get a human to read it and the per-file headers before any shipped code. Internal-only prototype is a much lower bar than redistribution. If it does not clear, the harvest pattern is still safe — schema ideas are not copyrightable, code is.

Do not import the Doctrine DDL. Symfony bundles of this vintage use annotation mapping with implicit conventions — naming strategies, join-table names, cascade rules — that produce surprising SQL. Translate to your own migrations and make soft-delete, audit-column, and nullability decisions explicitly.

The sibling bundle graph. Libre Informatique packages (blast-*, semantic-*, Sonata admin) tend to reference each other by class name. Pulling one bundle alone can leave unresolvable deps. Before any Symfony-side attempt, resolve the full graph in a scratch project.

You will not find a service provider. Don't grep for one, don't try composer require and wait for auto-discovery. If a teammate tried this, the empty boot is not a bug in the package.

Zero upstream support. Three stars and zero dependents means no CVE feed, no issues triage, no Stack Overflow answers. Assume you are the maintainer of any fork, and write an ADR so the "should we use this?" question doesn't resurface next quarter.

Framework version ceiling. Check the symfony/* and PHP pins in composer.json. An unmaintained bundle will fall behind Symfony's yearly majors within a release or two, which blocks your own upgrades if you bridge it.

Data ownership, decided on day one. If you run the standalone service, exactly one system writes CRM records. "Current owner" and dedup semantics are where eventual consistency quietly breaks — reads can be cached, writes should be queued, and the product must accept it explicitly.

Don't dual-boot a Symfony kernel in a Laravel process. Two containers, two ORMs, two config systems, shared request lifecycle — this is a known anti-pattern with unpredictable failure modes in queues and under load. If you need the code, run it out-of-process or reimplement.

Where the real extension points land in Laravel: custom casts for addresses and enums, observers for activity fan-out, policies for record visibility, and a Crm domain namespace as the seam for a future swap. Those are the transferable equivalents of the bundle's Symfony DI extension and configuration tree.

Debugging the harvest: when the reimplementation behaves differently from the Symfony original, bisect on invariants first (uniqueness of email per company, cascade on company deletion, whether an activity can have no subject). Those are where Doctrine magic tends to hide behavior you did not notice on read.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels