vespolina/core
Vespolina Core provides reusable PHP components for building e-commerce applications, including product, cart, order, pricing, and checkout abstractions. It offers a modular foundation for integrating commerce logic into custom PHP projects.
vespolina/core is a model/entity library, not a framework. It gives you PHP classes for the ecommerce domain (products, carts, orders, customers, prices, attributes) that you map and persist yourself. Treat it as a schema and vocabulary reference first, and as a dependency second.
Minimal steps:
Install and inspect what actually arrives. The repository is unknown in the metadata, so verify the package contents before trusting the namespace:
composer require vespolina/core --no-update
composer show -a vespolina/core # confirm versions, source, autoload
composer update vespolina/core
Then list the tree and find the model directory:
ls vendor/vespolina/core
find vendor/vespolina/core -maxdepth 4 -type d
Expect a PSR-0-era layout such as lib/Vespolina/Model/ or src/Vespolina/... with Product, Cart, CartItem, Order, OrderItem, Customer, Price, and a set of *Interface files. Confirm the real namespace with grep -R "^namespace" vendor/vespolina/core.
Read the interfaces before the classes. The *Interface files are the stable contract; concrete classes are a reference implementation. Start with ProductInterface, CartInterface, OrderInterface, and PriceInterface. That tells you what your application must implement and what the package expects from you.
Run a standalone smoke test outside Symfony. This is the fastest way to learn the object graph:
$product = new Vespolina\Model\Product();
$product->setName('Widget');
$product->setSlug('widget');
$cart = new Vespolina\Model\Cart();
$cart->addItem($product, 1);
var_dump($cart->getTotal());
Adjust class names to whatever the package actually ships. If instantiation and totals work, the domain model is usable; persistence is the next problem.
Then decide on persistence. The core does not ship a service container configuration for your app. You will register Doctrine mappings yourself (annotation, XML, or YAML) or extend the vendor classes as your own entities. Do the standalone test first so you are debugging the model, not the ORM.
First realistic use case: use the core’s Product/Cart/Order shapes to prototype a cart and checkout flow, then back it with your own Doctrine entities and repositories.
Interface-first, extend-second. For a prototype, extend the concrete classes (class Product extends Vespolina\Model\Product). For anything long-lived, implement the interfaces in your own entity classes and keep the vendor classes out of your inheritance tree. That gives you a clean exit if the package stays abandoned.
Doctrine integration. Because the package predates modern Symfony/Doctrine conventions, plan the mapping explicitly. If the vendor classes carry annotations, extending them as entities can force inheritance mapping you do not want. Two safe approaches:
ResolveTargetEntityListener so your associations point at your implementations of the vendor interfaces.Verify with doctrine:mapping:info and doctrine:schema:validate after every mapping change.
Cart and order workflow. Model the flow as: create cart → add product with quantity → recalculate totals on every mutation → convert to order at checkout. The critical detail is snapshotting: OrderItem should copy product name, SKU, unit price, and tax at order time. Never let an order’s total change because someone edited a product afterward.
Keep entities anemic. The package provides data holders. Put tax calculation, shipping selection, discount application, and total recalculation into your own services. If you are on Symfony 2.x, the event dispatcher is a reasonable place to hook cart events (cart.item_added, checkout.completed); otherwise, use a small domain service.
Repositories, not magic finders. Write explicit repositories for the lookups you actually need: product by slug or SKU, cart by session/token, order by number. Do not rely on the vendor model for query logic.
Money handling. Check the Price implementation before committing to it. If it stores floats, normalize to integer minor units at your boundary and do arithmetic there. Do not let floating-point totals creep into invoices.
Product modeling. Prefer explicit fields for the attributes you query and sort on; use the attribute/taxonomy classes for descriptive metadata. Avoid turning the attribute system into a full EAV model unless the catalog genuinely needs arbitrary attributes.
Testing. Build fixtures with Doctrine data fixtures or Alice, and unit-test cart totals and order conversion without a database. Those are the behaviors most likely to break and the easiest to isolate.
each(), and assumptions about Symfony 2.3–2.8. On PHP 8 you will likely need patches or a fork. Check composer.json for php and symfony/* constraints before installing into a modern app.composer show -a and the installed composer.json. Do not assume the namespace from the package name.doctrine:schema:tool starts generating joined-table inheritance or duplicate columns, you extended a mapped entity instead of mapping your own class. Switch to mapped superclasses, explicit mapping, or composition.composer.lock. For long-term use, fork the repository and reference it as a VCS/path repository so a Packagist disappearance cannot break your build.Price uses floats or formatted strings, wrap it in your own value object immediately. Currency and rounding bugs are expensive to fix later.get_class($entity) to spot proxy classes, doctrine:query:sql to inspect what is actually persisted, and var_dump on the cart object graph to confirm item totals before blaming the ORM.*Interface in your own class and keep the vendor code untouched. That is the only reliable upgrade and exit path for a package this old.How can I help you explore Laravel packages today?