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

Varieties Bundle Laravel Package

libre-informatique/varieties-bundle

A Symfony bundle for managing plant varieties, helping you model, organize, and expose variety data in agricultural or botanical applications. Integrates with Doctrine entities and provides a flexible foundation for variety catalogs and related taxonomy.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this before you type a command: this is a Symfony bundle, not a Laravel package. There is no service provider, no config/varieties-bundle.php, no facade, no auto-discovery. Running composer require libre-informatique/varieties-bundle inside a Laravel app will install a bundle that nothing wires up — you'll get PHP files, Doctrine XML mappings, and Symfony form classes that are dead weight in your app. For a Laravel project, the correct mental model is: this is a schema and domain-design reference you read, then port to Eloquent. The day-to-day value is the Family → Genus → Species → Subspecies → Variety hierarchy and the naming/convention decisions baked into it — not runtime code.

Where to look first (in the repo, on GitHub):

  1. src/Entity/ — the taxonomy chain. Read Family, Genus, Species, Subspecies, Variety in that order. Note the parent associations, the naming fields (scientific name, common name, authority/synonym handling if present), and any __toString() methods (those reveal the intended display label per level).
  2. Resources/config/doctrine/*.orm.xml (older Librinfo bundles use XML mappings, not attributes) — the actual column types, nullability, indexes, and join columns. This is your best source for the physical schema.
  3. Repository/ classes — reveal the intended query patterns (lookup by parent, tree traversal, slug/name search).
  4. Admin classes (Sonata Admin or EasyAdmin, depending on era) — the real documentation of intended CRUD flow and which fields are editable vs. derived.
  5. Form/ types — show field-level validation and which attributes are required at create vs. update.
  6. DependencyInjection/ + Resources/config/services.xml — confirms Symfony coupling; you'll mostly ignore this for a port.

First practical use case: a seed/cultivar catalog. Model it so that Variety is the sellable, referencable unit, and everything above it is read-only classification metadata. Seed lots, products, and producer records point at a Variety (or Species when no variety is known). Get that directionality right at the start — pointing products at Species "for flexibility" is the single most common way to corrupt the model later.

Minimal Eloquent port to start from (this is the whole of the "getting started" work for a Laravel app):

// One table per level keeps each level's attributes clean and mirrors the bundle's shape.
Schema::create('families', function (Blueprint $t) {
    $t->id();
    $t->string('name')->unique();        // e.g. "Solanaceae"
    $t->string('authority')->nullable(); // nomenclatural authority string
    $t->timestamps();
});

Schema::create('genera', function (Blueprint $t) {
    $t->id();
    $t->foreignId('family_id')->constrained();
    $t->string('name');
    $t->string('authority')->nullable();
    $t->timestamps();
    $t->unique(['family_id', 'name']);
});

Schema::create('species', function (Blueprint $t) {
    $t->id();
    $t->foreignId('genus_id')->constrained();
    $t->string('epithet');               // "lycopersicum"
    $t->string('authority')->nullable();
    $t->timestamps();
    $t->unique(['genus_id', 'epithet']);
});

// subspecies and varieties follow the same parent-link + unique-on-(parent, name) shape.

That's a weekend of work, and it gives you a schema you own — with full control over the naming, synonym, and authority fields the bundle hints at but doesn't fully resolve.

If you genuinely are on Symfony (the only case where this is a real dependency): register Librinfo\VarietiesBundle\LibrinfoVarietiesBundle in config/bundles.php, confirm Doctrine picks up the XML mapping, then run bin/console doctrine:schema:update --dump-sql and read the SQL before executing. The bundle predates migrations-first workflows; you want to convert that dump into a real migration rather than letting --force mutate your schema. Assume you'll need to pin compatible Symfony/Doctrine versions — check composer.json for the constraint range first, and expect it to be several majors behind current.

Implementation Patterns

Port the five-level chain as an adjacency list, not nested sets. The bundle models taxonomy as parent-child references per level. Keep that: a variety belongs to a subspecies (optional), which belongs to a species, which belongs to a genus, which belongs to a family. Adjacency lists are cheap to query for the depth-1 lookups you actually do ("all species in this genus", "all varieties of this species") and trivial to mutate when taxonomy is revised — which it is, constantly. Nested sets look appealing on paper and are miserable the first time a species is reclassified. If you later need full-tree reads, add a materialized path column (e.g. family/genus/species/variety slugs) and index it, rather than restructuring.

Model the hierarchy as separate tables per level (as above), not one self-referencing taxa table with a rank enum. The prior assessment's "adjacency" hint is right about relationships, but the bundle's structure is one entity per rank — and that's the correct call for a port. A single polymorphic taxa table forces every level's attributes into nullable columns and turns every query into where('rank', ...) noise. Separate tables give you real foreign keys, clean per-level validation, and readable relationships ($variety->species->genus->family). The cost is five small tables instead of one; the benefit is that your model still makes sense when "subspecies" turns out to need its own attributes.

Keep the Variety as the foreign-key target for everything transactional. Seed lots, inventory items, orders, producer registrations — all point at varieties.id. Add a nullable fallback species_id (or a polymorphic reference) only for the genuinely-species-level records, and enforce the rule in code that variety wins when present. This is the single pattern that keeps a catalog coherent as it grows.

Put the "naming" concern in its own table, not on the level tables. The bundle description calls out "taxonomy and naming support" — the lesson to carry over is that a taxon has multiple names over time: scientific name, accepted name, synonyms, common/marketing names, breeder names. Model that as a names table (taxon_type, taxon_id, name, type enum, is_preferred, authority, valid_from) rather than five name columns. This is exactly the "later workstream" the source product under-scoped, and it's where a naive port will bite you in month three.

Mirror the CRUD flow, not the admin framework. Whatever admin classes exist in the bundle encode an intended workflow: create Family → then Genus scoped to it → then Species → etc., with dependent selects. Reproduce that as Laravel form requests with scoped validation (Rule::exists('genera', 'id')->where('family_id', $request->family_id)) and a cascading select component (Livewire/Inertia + Alpine). Don't recreate Sonata Admin; recreate the flow. The workflow is the valuable part.

Seed from a maintained authority, not from the bundle. The bundle ships (or implies) no authoritative taxonomy content. Seed your tables from POWO / IPNI / GBIF / EPPO via a small import command — a taxonomy:import {source} artisan command writing through the same models you built. Keep the source identifier (source, external_id) on every row so you can re-sync when a species is renamed or split. Never treat taxonomy rows as user-authored once seeded; user variation goes on the names table and on varieties as local names.

Use repositories/scopes for the queries the UI actually runs. Family::with('genera')->..., Genus::where('family_id', $id)->orderBy('name'), Variety::whereHas('species', fn($q) => $q->where('genus_id', $id)). Build these as query scopes on the models: Genus::forFamily($familyId), Variety::forGenus($genusId). This is the direct translation of the bundle's repository classes, and it keeps controllers thin.

Extension points to preserve when porting: soft deletes on every level (taxonomy rows get merged/retired — you need history), created_by/updated_by, and a notes/synced_at pair per row to track reconciliation against the external authority. The bundle lets you extend Doctrine mappings; in Eloquent, the equivalent is keeping your schema additive and the models unsealed (no $guarded = [] but no over-constrained $fillable either).

Gotchas and Tips

  • The license is NOASSERTION. This is not an SPDX-recognised license. Before you copy any code — even a schema file — get legal sign-off, or reimplement the model from the public taxonomic references (POWO, IPNI, GBIF) instead. Reimplementing the five-table hierarchy is a day of work; the liability from copying unclear-licensed code into a product is not.

  • Explicitly DEPRECATED, 0 stars, 0 dependents. There are no security patches, no issue triage, no PHP-version guarantee. Treat "we depend on this" as equivalent to "we forked this and own it forever." For anything customer-facing, that should be a hard no.

  • No service provider means no php artisan package:discover entry. If you composer require this into Laravel expecting it to register, you'll debug for an hour before realising the bundle is Librinfo\VarietiesBundle\LibrinfoVarietiesBundle — a Symfony Bundle subclass that Laravel's container has no idea how to instantiate. There is no config, no publish tag, no facade, nothing. That's not a bug; it's a different framework.

  • Doctrine XML mappings, not attributes. Older Librinfo code predates PHP 8 attributes and Symfony's #[ORM\Entity]. If you inspect the repo you'll find Resources/config/doctrine/*.orm.xml (or YAML) mappings. If you're porting from those files, read them carefully: nullable="false", unique constraints, and length attributes live there and won't appear in the entity classes. This is where the real schema is.

  • doctrine:schema:update --force is a trap. The bundle's intended install flow was schema:update, which is now a deprecated/unsafe workflow. If you're on Symfony, dump the SQL (--dump-sql), review it, and convert to a proper migration before touching a real database. On Laravel, you'll be writing migrations anyway — just make sure the columns and indexes mirror the ORM XML rather than the entity annotations (they can drift).

  • Taxonomy isn't user data — don't let it become user data. The bundle's admin CRUD makes it look like maintainable content, inviting editors to edit species names directly. That's how you get twenty spellings of "Solanaceae" and a catalog nobody can join against. Enforce: taxonomy tables are written only by the import command (and a small admin for local additions), never by the catalog editing UI. Local names go to the names table.

  • "Variety" is overloaded — pin the meaning before you build. In horticulture, "variety" is a marketing/cultivar unit; in botany it's a rank below subspecies. The bundle models the taxonomic rank. If your product actually needs cultivars (breeder rights, PBR, registration), that's a parallel concept to the taxonomic variety — model it as a separate cultivars table linked to varieties, not forced into the same row. This is the single biggest modelling decision to get right up front.

  • Watch the naming/authority convention. Botanical names carry an authority string ("Solanum lycopersicum L.") and a year. If you flatten that into a single name column you can't reconcile against external authorities later. Keep epithet/name and authority as separate columns from day one; it's cheap now and painful to backfill.

  • Performance: cache the taxonomy tree aggressively, invalidate on import. Five tables of static data are trivially cacheable. Cache the whole tree as one key (Cache::remember('taxonomy.tree', ...)), bump the key on import. Do not run recursive parent queries per row in a listing page — the adjacency model makes that an N+1 that's invisible in dev and lethal at 10k list rows.

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