<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aimeos</title>
    <description>The latest articles on DEV Community by Aimeos (@aimeos).</description>
    <link>https://dev.to/aimeos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F203414%2Ff8316571-2fde-46f7-bc09-cc4561d38a21.png</url>
      <title>DEV Community: Aimeos</title>
      <link>https://dev.to/aimeos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aimeos"/>
    <language>en</language>
    <item>
      <title>How to implement a multi-vendor e-commerce marketplace using Laravel</title>
      <dc:creator>Aimeos</dc:creator>
      <pubDate>Thu, 25 Jun 2026 11:09:19 +0000</pubDate>
      <link>https://dev.to/aimeos/how-to-implement-a-multi-vendor-e-commerce-marketplace-397a</link>
      <guid>https://dev.to/aimeos/how-to-implement-a-multi-vendor-e-commerce-marketplace-397a</guid>
      <description>&lt;p&gt;A marketplace is a multi-tenant system that happens to sell things. That sentence is the whole implementation problem in one line. The seller dashboard looks like the hard part during planning, but the real work shows up around week three, when every query in the system suddenly has to answer a new question. Whose data is this? Which vendor owns this product, this order line, this stock level, this price? The storefront was never the issue. The issue is that a marketplace is multi-tenant from the first table, and most shops are not.&lt;/p&gt;

&lt;p&gt;So the first decision you make is also the one that costs the most if you get it wrong: do you build the tenancy yourself, or start from a backend that already has it. Retrofitting isolation onto a single-tenant shop is not a feature you add — it's a rewrite you discover. You thread a &lt;code&gt;vendor_id&lt;/code&gt; through models, then through queries, then through the cart, then through the admin, and every place you forget becomes a data leak where one seller sees another seller's orders. This guide takes the other path: build on Aimeos, a Laravel ecommerce package that's multi-tenant from the data model up, and spend your time on the marketplace instead of the plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide where tenancy lives
&lt;/h2&gt;

&lt;p&gt;Before any code, pick the foundation honestly, because it determines how much of the next month you spend on isolation you'll never show a user.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Vendor isolation&lt;/th&gt;
&lt;th&gt;Self-service onboarding&lt;/th&gt;
&lt;th&gt;Scales to thousands of vendors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build it yourself&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;you thread it through every query&lt;/td&gt;
&lt;td&gt;you build it&lt;/td&gt;
&lt;td&gt;you re-architect for it later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single-tenant shop + add-on&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;bolted onto a single-tenant base&lt;/td&gt;
&lt;td&gt;extension-dependent&lt;/td&gt;
&lt;td&gt;medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Aimeos&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;built into the data model, scoped per site&lt;/td&gt;
&lt;td&gt;one env flag&lt;/td&gt;
&lt;td&gt;designed for it, 1 to more than 1 billion items&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Build it yourself&lt;/strong&gt; looks cheapest on a whiteboard and turns out to be the most expensive. Tenancy is a cross-cutting concern: it touches every model, every join, every report and every API endpoint, and the cost isn't the scoping code you write — it's the one clause you forget. A single missing filter in a join is one vendor reading another's orders, and you find it in production, not review. It also never stays finished. Every feature you add afterwards has to re-earn its isolation, so you end up maintaining a multi-tenancy framework as an unpaid side project to the marketplace you actually wanted to build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single-tenant shop + add-on&lt;/strong&gt; moves the problem rather than solving it. The base assumes one merchant, so the extension grafts vendor columns and filters onto a schema that was never designed for them. Isolation then lives in the add-on's discipline instead of the data layer — it holds where the extension remembered to apply it and leaks at the edges the base shop reaches directly: admin tools, exports, and third-party plugins that have never heard of vendors. On top of that you inherit the add-on's release cycle and price, and you're betting it keeps pace with the platform it's bolted to.&lt;/p&gt;

&lt;p&gt;The rest of this guide assumes the third row, where isolation is a property of the data model rather than something layered on top. Each step is a flag or a small amount of config, not a sprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stand up the backend
&lt;/h2&gt;

&lt;p&gt;You need PHP 8.2+ and a database (MySQL, MariaDB, PostgreSQL or SQL Server). The distribution wires the storefront, the admin, the APIs and a seeded catalog in one command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project aimeos/aimeos myshop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Answer the prompts for database, mail and the admin account, then look around:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;myshop
php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The storefront is at &lt;code&gt;http://127.0.0.1:8000&lt;/code&gt;, the admin at &lt;code&gt;http://127.0.0.1:8000/admin&lt;/code&gt;. At this point you have a working single shop. The next two steps turn it into a marketplace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable multi-vendor mode
&lt;/h2&gt;

&lt;p&gt;The multi-vendor machinery ships in the box. You switch it on with one line in &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SHOP_MULTISHOP=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives every vendor a scoped storefront and a scoped backend at their own &lt;em&gt;site&lt;/em&gt; — Aimeos's word for a tenant. You can see what the flag did in the routing: every route gains a &lt;code&gt;{site}&lt;/code&gt; segment, so the storefront, the JSON:API, the admin panels and the seller's own backend all scope to the vendor in the URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'prefix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'admin/{site}/jqadm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s1"&gt;'middleware'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'web'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'auth'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'verified'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;span class="s1"&gt;'graphql'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'prefix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'admin/{site}/graphql'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'middleware'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'web'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'auth'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'verified'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;span class="s1"&gt;'jsonapi'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'prefix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{site}/jsonapi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="s1"&gt;'middleware'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'web'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'api'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;span class="s1"&gt;'default'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'prefix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{site}/shop'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="s1"&gt;'middleware'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'web'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;span class="s1"&gt;'supplier'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'prefix'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{site}/s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="s1"&gt;'middleware'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'web'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;{site}&lt;/code&gt; placeholder isn't there for pretty URLs. It's the seam that keeps one vendor's administration from reaching into another's, all the way down to the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let vendors onboard themselves
&lt;/h2&gt;

&lt;p&gt;Creating each seller account by hand doesn't scale past a demo. Turn on self-registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SHOP_REGISTRATION=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A newly registered seller gets administrator rights over their own site and nothing else — their catalog, their orders, their customers, their settings, none of anyone else's. If that's more power than you want to hand a fresh signup, drop the default permission level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SHOP_PERMISSION=editor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if neither "admin" nor "editor" maps to the access you have in mind, the permission sets are themselves configuration. Publish your own version of the admin resource config and decide exactly which panels and actions each role can touch. The platform operator keeps the root; vendors get precisely the slice you grant them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the isolation you just got
&lt;/h2&gt;

&lt;p&gt;It's worth knowing why the two flags above are enough, so you trust the result instead of double-checking it forever. Aimeos organizes everything around the site. Every record the system stores — products, prices, orders, stock — carries a site id, and the data layer filters on it automatically. You never write the &lt;code&gt;where siteid = ?&lt;/code&gt; clause; the manager that loads the data already added it. That single design decision is what turns "build a marketplace" from a tenancy project back into a normal shop project.&lt;/p&gt;

&lt;p&gt;Sites form a tree rather than a flat list, so a parent can hold shared catalog, categories or configuration that child vendors inherit, while each vendor still owns and overrides their own. A platform operator runs the root; vendors live underneath it with their data sealed off from each other by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give each vendor a catalog and a storefront
&lt;/h2&gt;

&lt;p&gt;Each seller logs into a backend that looks like a complete shop because, scoped to their site, it is one. They manage their own products with the full Aimeos catalog model behind them — variants, bundles, vouchers, virtual and downloadable goods, configurable and custom products, and subscriptions with recurring payments. A marketplace rarely sells one shape of thing; the moment one vendor wants tiered B2B pricing while another sells monthly boxes and a third sells license keys, a thin product table becomes the bottleneck. Aimeos models all of it without schema surgery, so you don't have to say no to a seller's business model.&lt;/p&gt;

&lt;p&gt;On the storefront side, each vendor has a public supplier page (the &lt;code&gt;/s&lt;/code&gt; route from Step 2) — a branded presence inside the marketplace with their catalog, profile and listings. Buyers shop across vendors; the basket and checkout are shared; the data behind each line stays attributed to the vendor that owns it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle the money
&lt;/h2&gt;

&lt;p&gt;This is the step teams put off and regret. The marketplace-specific concerns — routing the lines of a mixed-vendor order to the right sellers, commissions, and seller payouts — live in a dedicated &lt;a href="https://aimeos.com/extensions" rel="noopener noreferrer"&gt;marketplace extension&lt;/a&gt; that builds on the multi-vendor core. The core gives you isolation and per-vendor administration for free; the extension adds the commercial layer of running a platform that takes a cut.&lt;/p&gt;

&lt;p&gt;Settle the money at the gateway rather than reconciling it by hand afterward. Aimeos ships payment extensions built for marketplaces — Stripe among them — that split each transaction the moment it's captured, sending the platform its commission and forwarding the remainder to the seller who fulfilled the line. Payouts stop being a monthly spreadsheet exercise and become a property of checkout: the buyer pays once, and the money lands where it's owed, already divided. The same model covers the awkward cases a marketplace inherits — refunds that have to claw back the right share, and orders spanning several vendors that need several payees from a single basket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plan for scale before you need it
&lt;/h2&gt;

&lt;p&gt;Marketplaces don't fail at launch. They fail when they succeed — when a few dozen vendors become a few thousand, each with thousands of SKUs, and the catalog that felt instant at 5,000 products crawls at five million. Scale is the requirement that's invisible on day one and existential by month eighteen.&lt;/p&gt;

&lt;p&gt;You don't implement anything extra here; you inherit it. Aimeos runs the same architecture from a single seller up past a billion items, with render times down to around 20ms and response times near 100ms on a properly built catalog. The performance work a hand-rolled marketplace postpones until it's an emergency is the work this backend already did, so growth stays a business problem instead of becoming a database firefight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go headless where it pays
&lt;/h2&gt;

&lt;p&gt;Vendors increasingly want their own tooling — a mobile app to manage listings, a custom analytics view, an ERP that pushes inventory. Aimeos is API-first underneath the bundled storefront: a JSON:API following jsonapi.org for the buyer side and a GraphQL API for administration, both already scoped per site. Drive the whole marketplace headless, give vendors programmatic access to their own data, or keep the included storefront and only go headless where it pays. None of those are a rebuild — they're the same backend addressed differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify it
&lt;/h2&gt;

&lt;p&gt;The claim worth testing is the boring one: that the isolation is real and you didn't have to write it. With &lt;code&gt;SHOP_MULTISHOP=true&lt;/code&gt; and &lt;code&gt;SHOP_REGISTRATION=true&lt;/code&gt; set, create a couple of vendor accounts, sign in as each, and confirm the obvious thing from the inside — neither one can see the other's catalog or orders, and nobody wrote a scoping clause to make that true.&lt;/p&gt;

&lt;p&gt;The live &lt;a href="https://laravel.demo.aimeos.org" rel="noopener noreferrer"&gt;frontend demo&lt;/a&gt; and &lt;a href="https://admin.demo.aimeos.org" rel="noopener noreferrer"&gt;admin demo&lt;/a&gt; are up if you want a look first, the &lt;a href="https://aimeos.org/docs/latest/laravel" rel="noopener noreferrer"&gt;docs&lt;/a&gt; cover the multi-vendor setup end to end, and the &lt;a href="https://aimeos.com/extensions" rel="noopener noreferrer"&gt;marketplace extension&lt;/a&gt; is where the commission and payout layer lives.&lt;/p&gt;

&lt;p&gt;Implementing a marketplace is mostly about not implementing the parts that should already exist. Start from a backend that's multi-tenant by design, and the thing you ship is the marketplace — not the plumbing underneath it.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>ecommerce</category>
      <category>webdev</category>
    </item>
    <item>
      <title>PagibleAI: A CMS for your Laravel application</title>
      <dc:creator>Aimeos</dc:creator>
      <pubDate>Mon, 22 Jun 2026 12:15:09 +0000</pubDate>
      <link>https://dev.to/aimeos/pagibleai-a-cms-for-your-laravel-application-3mh4</link>
      <guid>https://dev.to/aimeos/pagibleai-a-cms-for-your-laravel-application-3mh4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi13bdgno3bhgytko9e6e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi13bdgno3bhgytko9e6e.jpg" alt="Cover image" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add content management to a Laravel app you already run. Pagible is a Composer package that brings structured content, versioning, GraphQL and JSON:API, an AI editor, and an MCP server — without taking over your app.&lt;/p&gt;




&lt;p&gt;Every Laravel project hits the same question eventually. The client wants to edit the content themselves, and you have to decide who owns the application.&lt;/p&gt;

&lt;p&gt;None of the usual answers are clean.&lt;/p&gt;

&lt;p&gt;Run WordPress in headless mode and you take on a second runtime, a second auth system, and a REST API you didn't design. Adopt a hosted platform like Contentful and you pay per record while your content lives on someone else's servers. Build a bespoke admin panel and you spend the next two months rebuilding a tree editor, a media library, and a draft/publish flow instead of shipping the product.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;What you take on&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Headless WordPress&lt;/td&gt;
&lt;td&gt;A second runtime, a second auth system, a REST API you didn't design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosted platform (Contentful)&lt;/td&gt;
&lt;td&gt;Per-record pricing, your content on someone else's servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bespoke admin panel&lt;/td&gt;
&lt;td&gt;Months rebuilding a tree editor, media library, and publish flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PagibleAI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One Composer package; your app stays yours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://pagible.com" rel="noopener noreferrer"&gt;PagibleAI&lt;/a&gt; starts from a different premise: add content management to a Laravel app you already run. You &lt;code&gt;composer require&lt;/code&gt; it, and your app stays your app. The CMS moves in as a set of packages, brings its own models, migrations, admin panel, and APIs, and stays out of the way the rest of the time.&lt;/p&gt;

&lt;p&gt;Here's what stands out from a developer's seat.&lt;/p&gt;

&lt;h2&gt;
  
  
  The install is a normal Composer package
&lt;/h2&gt;

&lt;p&gt;There's no service to host and nothing external to wire up. If you have a Laravel 11, 12, or 13 app on PHP 8.2+, you add the package and run the migrations. That's the whole install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require aimeos/pagible
php artisan cms:install
php artisan migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then grant yourself editing rights:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan cms:user &lt;span class="nt"&gt;-e&lt;/span&gt; editor@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The admin panel is served from your own app at &lt;code&gt;/cmsadmin&lt;/code&gt;. It's a Vue 3 and Vuetify single-page app talking to a GraphQL endpoint. No external dashboard, no webhook round-trips, no second deployment to keep in sync.&lt;/p&gt;

&lt;p&gt;Underneath, it's a monorepo split into focused packages: core models, admin, AI, GraphQL, JSON:API, search, MCP, backup, import, and theming. They share the &lt;code&gt;Aimeos\Cms\&lt;/code&gt; namespace. You install one meta-package and get all of it, but each concern stays isolated, so the surface you have to reason about stays small.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it's built on
&lt;/h2&gt;

&lt;p&gt;There's not much exotic here. Each capability leans on a package the Laravel community already knows, so most of the stack is familiar before you read a line of Pagible's own code.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;laravel/framework&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Laravel 11 / 12 / 13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nuwave/lighthouse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GraphQL admin API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;laravel-json-api/laravel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON:API content delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;laravel/scout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full-text search engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;laravel/mcp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MCP server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;laravel/reverb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Real-time broadcasting (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;laravel/cashier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Payments and paywalls — Stripe, Paddle, Mollie (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aimeos/prisma&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Broad LLM provider integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aimeos/laravel-nestedset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optimized nested-set page tree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;intervention/image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Image processing and previews&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;league/commonmark&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Markdown rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ezyang/htmlpurifier&lt;/code&gt;, &lt;code&gt;enshrined/svg-sanitize&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Content sanitization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The admin panel is its own front end on top: Vue 3, Vuetify, and Vite, built into a bundle that ships with the package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content is structured, not a wall of HTML
&lt;/h2&gt;

&lt;p&gt;This is what sets Pagible apart from the WordPress lineage. A page isn't one big rich-text field. It's a node in a tree that holds an ordered list of typed content elements.&lt;/p&gt;

&lt;p&gt;Three models carry the weight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Page&lt;/strong&gt; — a node in a nested-set tree, with drag-and-drop reordering, UUIDs, and soft deletes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Element&lt;/strong&gt; — a content block with a &lt;code&gt;type&lt;/code&gt; and a &lt;code&gt;data&lt;/code&gt; payload. Pages reference their elements in order, and the same element can be shared across many pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File&lt;/strong&gt; — media, with AI-generated descriptions and automatically sized previews.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cms_pages  (nested-set tree)
└─ Page ─┬─ ordered elements ── cms_elements   typed, reusable blocks
         ├─ attached files   ── cms_files      media + AI descriptions
         └─ every change     ── cms_versions   immutable snapshots
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A content element is a small, typed structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'id'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ByC2ze'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'type'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'group'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'data'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'text'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'## A heading\n\nSome **markdown** body copy.'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each &lt;code&gt;type&lt;/code&gt; maps to a Blade partial in the theme. The entire renderer for a text element in e.g. theme/views/text.blade.php is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@markdown($data-&amp;gt;text ?? '')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get a generous set of ~30 content element types out of the box including:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Renders&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Markdown body copy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hero&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full-width banner with background media&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cards&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Grid of linked cards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image-text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Image alongside text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pricing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pricing tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;slideshow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Image carousel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Syntax-highlighted snippets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;questions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FAQ accordion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Adding your own type means defining its data shape and writing the partial that renders it. You keep full control of the markup, and you can override any view or drop in your own components.&lt;/p&gt;

&lt;p&gt;One detail matters for security. The mapping from a type to its handler comes from a trusted schema keyed by the type name, never from the stored element data. A content author can't smuggle a different handler into a record's payload, which closes a whole class of injection problems before it starts.&lt;/p&gt;

&lt;p&gt;The payoff is the one Contentful built a business on. Your front end consumes predictable, typed data instead of parsing arbitrary HTML. The same content can render as a web page, feed a mobile app, or drive a sitemap, with no one copy-pasting markup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two APIs, pointed in opposite directions
&lt;/h2&gt;

&lt;p&gt;Pagible is API-first in a literal sense. The admin panel is just one client of the API, not a privileged path into the database.&lt;/p&gt;

&lt;p&gt;Editing runs over &lt;strong&gt;GraphQL&lt;/strong&gt;, built on Lighthouse. It's the read-write surface the admin uses, and the same one your own tooling, scripts, or integrations can talk to.&lt;/p&gt;

&lt;p&gt;Delivery runs over a read-only &lt;strong&gt;JSON:API&lt;/strong&gt; that follows the spec, tuned for the public side. The split is deliberate. The write API can afford to be expressive; the read API is built to serve content fast and cache hard. Rendered pages are cached per-page with a configurable TTL, a day by default, so the common case never touches the editing machinery.&lt;/p&gt;

&lt;p&gt;Prefer to render server-side? The theme package ships Blade templates and small directives like &lt;code&gt;@markdown&lt;/code&gt; and &lt;code&gt;@localDate&lt;/code&gt;, and all of it is yours to override.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioning an editorial team can trust
&lt;/h2&gt;

&lt;p&gt;Every change to a page, element, or file becomes an immutable snapshot in &lt;code&gt;cms_versions&lt;/code&gt;. Editors see the latest draft. The public sees the most recently published version. Those are two different things, on purpose.&lt;/p&gt;

&lt;p&gt;That model gives you the workflow features for free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save a draft without touching the live site.&lt;/li&gt;
&lt;li&gt;Schedule a version to go live later. &lt;code&gt;cms:publish&lt;/code&gt; promotes anything whose &lt;code&gt;publish_at&lt;/code&gt; has passed, on a schedule.&lt;/li&gt;
&lt;li&gt;Revert to any earlier snapshot when an edit goes wrong.&lt;/li&gt;
&lt;li&gt;Keep a bounded history (ten versions per item by default) as an audit trail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For teams, there's an unsaved-changes guard, concurrent-edit protection, and a Git-like three-way merge when two people touch the same content. A half-finished draft is never one stray click from production.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI is wired in per task, not bolted on as a chatbot
&lt;/h2&gt;

&lt;p&gt;The "AI" in PagibleAI isn't one assistant sitting in a corner. It's a set of content operations, each routed to whichever provider does that job best, configured by environment variable.&lt;/p&gt;

&lt;p&gt;The defaults look like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Write and refine copy, describe media&lt;/td&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generate and edit images (inpaint, repaint)&lt;/td&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Translate&lt;/td&gt;
&lt;td&gt;DeepL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Erase, isolate background, uncrop, upscale&lt;/td&gt;
&lt;td&gt;Clipdrop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transcribe audio&lt;/td&gt;
&lt;td&gt;OpenAI Whisper&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each task reads its own &lt;code&gt;CMS_AI_{TASK}&lt;/code&gt; / &lt;code&gt;_MODEL&lt;/code&gt; / &lt;code&gt;_API_KEY&lt;/code&gt; trio. You configure only the providers you use, and you can point any task at a different model or provider without touching code. There are batch commands too. &lt;code&gt;cms:description&lt;/code&gt;, for example, walks through pages and media that lack descriptions and writes SEO meta text and alt text for them.&lt;/p&gt;

&lt;p&gt;Translation is the standout. Content can be translated into more than 35 languages while keeping its structured shape intact, so a translated page is still a real page, not a flattened copy. The whole AI layer is there when you want it and out of the way when you don't — it's truly optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  The MCP server is the genuinely new part
&lt;/h2&gt;

&lt;p&gt;This is where Pagible reads the room. The same operations the admin panel performs are exposed as &lt;a href="https://pagible.com/configure-mcp" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; tools — 33 of them, covering pages, elements, files, image editing, translation, and search.&lt;/p&gt;

&lt;p&gt;The result: an assistant like Claude can run the CMS the way an editor would. Not by scraping a UI or guessing at an undocumented API, but through typed tools with real authorization behind them.&lt;/p&gt;

&lt;p&gt;"Draft a pricing page from these bullet points, generate a hero image, translate it into German, and schedule it for Monday" stops being a feature request. It becomes a sequence of tool calls an agent makes against your content, with the same permission checks and versioning a human edit goes through.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The CMS turns into something an agent can operate, not just a UI a human clicks through.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It works because the architecture was structured-first from the start. When content is typed and the operations are explicit, an agent has something solid to act on. That's the upside of not storing everything as a wall of HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling, and the boring parts that matter
&lt;/h2&gt;

&lt;p&gt;The same install runs on a single SQLite file for a brochure site and on a database cluster for millions of pages. It's Octane-friendly and built for cloud-native deployment.&lt;/p&gt;

&lt;p&gt;Search reflects that range. A custom Laravel Scout engine speaks the native dialect of whatever database you're on: FTS5 on SQLite, &lt;code&gt;MATCH … AGAINST&lt;/code&gt; on MySQL, &lt;code&gt;tsvector&lt;/code&gt; on PostgreSQL, &lt;code&gt;CONTAINSTABLE&lt;/code&gt; on SQL Server. No separate search cluster for something the database already does.&lt;/p&gt;

&lt;p&gt;Multi-tenancy is single-database by design. Every model is scoped by a &lt;code&gt;tenant_id&lt;/code&gt; through a global Eloquent scope, resolved by the Laravel tenancy package you probably already use. Multi-domain routing is a config flag. Real-time collaboration is opt-in over Laravel Reverb, broadcasting compact, metadata-only events so two editors don't clobber each other.&lt;/p&gt;

&lt;p&gt;The security posture is the kind you'd rather inherit than assemble: user content sanitized through HTMLPurifier, CSP headers enforced, hCaptcha on forms, URL validation on submitted links, and rate limiting across the API.&lt;/p&gt;

&lt;p&gt;Migrating in? There's a WordPress importer that moves a site over with an Artisan command, plus per-tenant backup and restore with integrity checks.&lt;/p&gt;

&lt;p&gt;Want to charge for content? A separate &lt;code&gt;aimeos/pagible-cashier&lt;/code&gt; package wires Pagible into Laravel Cashier, so you can put pages behind a paywall or sell course access through Stripe, Paddle, or Mollie. Its installer prompts for a provider, writes the credentials, and adds the &lt;code&gt;Billable&lt;/code&gt; trait to your user model. Like the rest, it's opt-in — install it only if you need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who it's for, and the honest caveats
&lt;/h2&gt;

&lt;p&gt;Pagible fits when you have a Laravel app and want the people who own the content to manage it without you handing over the codebase. The headless APIs, structured content, and MCP tooling make it a solid base for anything where content feeds more than one front end.&lt;/p&gt;

&lt;p&gt;Two things to weigh. It's young and moving fast, and the AI features need third-party API keys, which means accounts and may need a budget if you want the full set.&lt;/p&gt;

&lt;p&gt;It's MIT licensed, so you can use it in commercial work with no per-site fees and no lock-in.&lt;/p&gt;

&lt;p&gt;None of the caveats change the central pitch, which is the thing I keep coming back to: it installs &lt;em&gt;into&lt;/em&gt; your app instead of asking your app to live inside it. If you've ever lost a weekend to a headless WordPress proxy, that alone is worth the &lt;code&gt;composer require&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;The code and docs are at &lt;a href="https://pagible.com" rel="noopener noreferrer"&gt;pagible.com&lt;/a&gt; and on &lt;a href="https://github.com/aimeos/pagible" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. If you try it, I'd like to hear which part of the workflow surprised you.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>cms</category>
      <category>ai</category>
    </item>
    <item>
      <title>A working Laravel shop in five minutes with Aimeos</title>
      <dc:creator>Aimeos</dc:creator>
      <pubDate>Thu, 18 Jun 2026 13:20:40 +0000</pubDate>
      <link>https://dev.to/aimeos/a-working-laravel-shop-in-five-minutes-with-aimeos-h0</link>
      <guid>https://dev.to/aimeos/a-working-laravel-shop-in-five-minutes-with-aimeos-h0</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjglxep2ckpgzgs0is4zs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjglxep2ckpgzgs0is4zs.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding a shop to a Laravel app usually turns into a quarter of work. You pick a cart library, bolt on a payment gateway, build an admin panel nobody asked to build, then discover your product model can't handle variants, bundles or tier prices without a rewrite. The ecommerce part eats the schedule; the Laravel part was never the problem.&lt;/p&gt;

&lt;p&gt;Aimeos takes the opposite route. It's a Laravel package that brings the storefront, the admin backend, a JSON:API for the frontend, a GraphQL API for administration, and a product model built for variants, bundles, subscriptions and block pricing. You install it the way you install anything else in Laravel, and you have a running shop before your coffee is cold.&lt;/p&gt;

&lt;p&gt;Here's the part people don't believe until they run it: the full setup is about five minutes. Two paths, depending on whether you're starting fresh or extending an app you already have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Path one: a standalone shop
&lt;/h2&gt;

&lt;p&gt;If you don't have an app yet, the distribution wires everything up for you. You need PHP 8.2+ and a database (MySQL, MariaDB, PostgreSQL or SQL Server).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project aimeos/aimeos myshop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During install it asks for your database credentials, your mail settings, and the email and password for the admin account. It runs the schema setup and seeds demo data as part of the same step, so when it finishes you have a populated catalog, not an empty shell.&lt;/p&gt;

&lt;p&gt;Start it with the built-in server to look around:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;myshop
php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The storefront is at &lt;a href="http://127.0.0.1:8000" rel="noopener noreferrer"&gt;http://127.0.0.1:8000&lt;/a&gt; and the admin backend at &lt;a href="http://127.0.0.1:8000/admin" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/admin&lt;/a&gt;. Log in with the account you just created. Catalog, baskets, checkout, orders and the dashboard are already there.&lt;/p&gt;

&lt;p&gt;For hosting, point your vhost document root at &lt;code&gt;myshop/public/&lt;/code&gt; and set &lt;code&gt;APP_URL&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt; to your domain. That's the only deployment-specific change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Path two: into an existing Laravel app
&lt;/h2&gt;

&lt;p&gt;Already have an app and want a shop inside it? Require the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require aimeos/aimeos-laravel:~2025.10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Publish the config and assets, run the migration, and let Aimeos set up its tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan vendor:publish &lt;span class="nt"&gt;--tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;config &lt;span class="nt"&gt;--tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public
php artisan migrate
php artisan aimeos:setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Append &lt;code&gt;--option=setup/default/demo:1&lt;/code&gt; to &lt;code&gt;aimeos:setup&lt;/code&gt; if you want demo data to click through; leave it off in production.&lt;/p&gt;

&lt;p&gt;Create a superuser for the backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan aimeos:account &lt;span class="nt"&gt;--super&lt;/span&gt; you@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prompts for a password. The shop now lives under your app's routes, sharing the same Laravel session, auth and config. The frontend renders at &lt;code&gt;/&lt;/code&gt; and the admin at &lt;code&gt;/admin&lt;/code&gt;, alongside whatever your app already serves.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you actually get
&lt;/h2&gt;

&lt;p&gt;The install is short because the package isn't a starting point you finish writing — it's the finished thing, built to be customized rather than completed. Out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A storefront with catalog filters, search, basket, multi-step checkout and customer accounts.&lt;/li&gt;
&lt;li&gt;A full admin backend with a dashboard for products, orders, customers, pricing and marketing.&lt;/li&gt;
&lt;li&gt;A JSON:API following jsonapi.org for the frontend, so you can drop the bundled views and drive it from React, Vue or a native app.&lt;/li&gt;
&lt;li&gt;A GraphQL API for administration if you'd rather manage data programmatically.&lt;/li&gt;
&lt;li&gt;A product model that handles variants, bundles, vouchers, virtual and configurable products, plus subscriptions with recurring payments and block/tier pricing — without schema surgery.&lt;/li&gt;
&lt;li&gt;Over 100 payment gateway integrations through Omnipay.&lt;/li&gt;
&lt;li&gt;Multi-language routing, multi-vendor and multi-warehouse support, translated into 30+ languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's designed to stay fast as the catalog grows, from a handful of products into the millions, which is the part that usually decides whether a shop survives its second year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing without forking
&lt;/h2&gt;

&lt;p&gt;Every layer is replaceable through Laravel's own conventions. Configuration lives in &lt;code&gt;config/shop.php&lt;/code&gt;. Templates are standard Blade views you override by copying them into &lt;code&gt;resources/views/vendor/shop/&lt;/code&gt;. Business logic sits in decorators you register in config, so you change behavior by wrapping it, not by editing vendor code that the next update would overwrite. Turn on multi-language routing, multi-vendor mode or self-registration for sellers with a single &lt;code&gt;.env&lt;/code&gt; flag each:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SHOP_MULTILOCALE=true
SHOP_MULTISHOP=true
SHOP_REGISTRATION=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The honest test is to run it yourself rather than take the word count on faith. &lt;code&gt;composer create-project aimeos/aimeos myshop&lt;/code&gt;, answer the prompts, &lt;code&gt;php artisan serve&lt;/code&gt;, and open the browser. If you want to see the result first, the &lt;a href="https://laravel.demo.aimeos.org" rel="noopener noreferrer"&gt;frontend demo&lt;/a&gt; and &lt;a href="https://admin.demo.aimeos.org" rel="noopener noreferrer"&gt;admin demo&lt;/a&gt; are live.&lt;/p&gt;

&lt;p&gt;Docs are at &lt;a href="https://aimeos.org/docs/latest/laravel" rel="noopener noreferrer"&gt;aimeos.org/docs/latest/laravel&lt;/a&gt;, and if you're building a headless or PWA frontend, the &lt;a href="https://github.com/aimeos/aimeos-headless" rel="noopener noreferrer"&gt;Aimeos headless distribution&lt;/a&gt; ships API-only with JWT auth already configured.&lt;/p&gt;

&lt;p&gt;Five minutes to a working shop. The rest of the quarter is yours to spend on the parts that make it yours.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>webdev</category>
      <category>php</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Stop juggling AI SDKs in PHP — meet Prisma</title>
      <dc:creator>Aimeos</dc:creator>
      <pubDate>Fri, 22 May 2026 12:00:17 +0000</pubDate>
      <link>https://dev.to/aimeos/stop-juggling-ai-sdks-in-php-meet-prisma-4b2</link>
      <guid>https://dev.to/aimeos/stop-juggling-ai-sdks-in-php-meet-prisma-4b2</guid>
      <description>&lt;p&gt;If you've ever integrated an AI provider into a PHP application, you know the drill. You pull in the OpenAI SDK. Then the client wants image generation, so you add StabilityAI — different SDK, different request format, different error handling. Then they want audio transcription — enter Deepgram, yet another integration. Before you know it, you're maintaining three separate API integrations, each with their own quirks, response formats, and retry logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prisma fixes this.&lt;/strong&gt; One package. One API. 25+ providers. Text, image, audio, and video.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require aimeos/prisma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What is Prisma?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/aimeos/prisma" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt; is a light-weight, MIT-licensed PHP package that provides a unified interface for working with AI providers across four media domains. No framework coupling. Just Guzzle under the hood, PHP 8.2+, and a clean fluent API.&lt;/p&gt;

&lt;p&gt;Here's the core idea — every provider works through the same interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aimeos\Prisma\Prisma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Generate text with OpenAI&lt;/span&gt;
&lt;span class="nv"&gt;$text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'openai'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Summarize the benefits of renewable energy'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Generate an image with StabilityAI&lt;/span&gt;
&lt;span class="nv"&gt;$image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stabilityai'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;imagine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a mountain landscape at sunset'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Transcribe audio with Deepgram&lt;/span&gt;
&lt;span class="nv"&gt;$transcript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'deepgram'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;transcribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$audioFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Describe a video with Gemini&lt;/span&gt;
&lt;span class="nv"&gt;$description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;video&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gemini'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$videoFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same pattern everywhere: &lt;code&gt;Prisma::{domain}()-&amp;gt;using(provider, config)-&amp;gt;{method}()&lt;/code&gt;. Switch providers by changing a single string. Your application code stays the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters in practice
&lt;/h2&gt;

&lt;p&gt;Imagine you're building a product listing tool. You need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate product descriptions from images&lt;/li&gt;
&lt;li&gt;Extract structured data (name, price, category) from the descriptions&lt;/li&gt;
&lt;li&gt;Generate marketing images for missing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without Prisma, that's three different SDK integrations. With Prisma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aimeos\Prisma\Prisma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aimeos\Prisma\Schema\Schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Describe a product image&lt;/span&gt;
&lt;span class="nv"&gt;$description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'openai'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$productPhoto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Extract structured data&lt;/span&gt;
&lt;span class="nv"&gt;$schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'product'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'category'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'electronics'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'clothing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'food'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'other'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gemini'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;structure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Extract product info from: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;structured&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// ['name' =&amp;gt; 'Wireless Headphones', 'price' =&amp;gt; 79.99, 'category' =&amp;gt; 'electronics']&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Generate a product image&lt;/span&gt;
&lt;span class="nv"&gt;$image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stabilityai'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;imagine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Professional illustration of &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three providers, three media types, one consistent API. And if tomorrow you want to swap Gemini for Anthropic, you change one line.&lt;/p&gt;

&lt;p&gt;You can also check provider capabilities at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'clipdrop'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$provider&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'upscale'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$provider&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;upscale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$lowResImage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Or throw if the capability is missing&lt;/span&gt;
&lt;span class="nv"&gt;$provider&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'upscale'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// throws NotImplementedException if not supported&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's new in 0.4
&lt;/h2&gt;

&lt;p&gt;Version 0.4 is a major release that adds text generation, structured output, tool calling, and a lot more. Here's what landed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Text generation across 14 providers
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;write()&lt;/code&gt; method brings text generation to Prisma with support for OpenAI, Anthropic, Gemini, Bedrock, Mistral, Groq, Cohere, Deepseek, Alibaba, xAI, Perplexity, OpenRouter, Ollama, and Deepseek. All multimodal — pass images, audio, or documents alongside your prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aimeos\Prisma\Base\File&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'anthropic'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withSystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'You are a helpful assistant'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Describe this image'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'photo.jpg'&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Structured output
&lt;/h3&gt;

&lt;p&gt;Get typed JSON responses from LLMs using a fluent Schema builder. Prisma uses each provider's &lt;strong&gt;native structured output API&lt;/strong&gt; — no prompt engineering hacks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aimeos\Prisma\Schema\Schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'event'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'date'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'date'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'attendees'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="s1"&gt;'location'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'city'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s1"&gt;'country'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'openai'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;structure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Parse: Team meetup on June 15 in Berlin with Alice and Bob'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;structured&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// [&lt;/span&gt;
&lt;span class="c1"&gt;//     'title' =&amp;gt; 'Team meetup',&lt;/span&gt;
&lt;span class="c1"&gt;//     'date' =&amp;gt; '2026-06-15',&lt;/span&gt;
&lt;span class="c1"&gt;//     'attendees' =&amp;gt; ['Alice', 'Bob'],&lt;/span&gt;
&lt;span class="c1"&gt;//     'location' =&amp;gt; ['city' =&amp;gt; 'Berlin', 'country' =&amp;gt; 'Germany'],&lt;/span&gt;
&lt;span class="c1"&gt;// ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Schema builder supports strings, integers, numbers, booleans, arrays, objects, enums (including PHP &lt;code&gt;BackedEnum&lt;/code&gt;), nested objects, &lt;code&gt;required()&lt;/code&gt;, &lt;code&gt;nullable()&lt;/code&gt;, &lt;code&gt;min()&lt;/code&gt;/&lt;code&gt;max()&lt;/code&gt;, regex &lt;code&gt;pattern()&lt;/code&gt;, and more. You can also pass raw JSON Schema arrays via &lt;code&gt;Schema::fromArray()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool calling with auto-execution
&lt;/h3&gt;

&lt;p&gt;This is where things get interesting. Prisma supports full agentic tool calling with an automatic execution loop. You define tools, the model calls them, Prisma executes the handlers and feeds results back — all automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aimeos\Prisma\Tools&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aimeos\Prisma\Schema\Schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$searchTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'search_products'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'Search the product database'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'query'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Search query'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s1"&gt;'limit'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Max results'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProductSearch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'limit'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'openai'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withTools&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$searchTool&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withMaxSteps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Find me the top 3 wireless headphones under $100'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// The model searched, got results, and wrote a summary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can inspect what happened during the tool loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;      &lt;span class="c1"&gt;// 'search_products'&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// ['query' =&amp;gt; 'wireless headphones', 'limit' =&amp;gt; 3]&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="c1"&gt;// '[ ... search results ... ]'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tools also support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-tool call limits&lt;/strong&gt;: &lt;code&gt;Tools::make(...)-&amp;gt;max(3)&lt;/code&gt; — the tool can only be called 3 times&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom error handlers&lt;/strong&gt;: &lt;code&gt;-&amp;gt;failed(fn($e, $args) =&amp;gt; 'Fallback message')&lt;/code&gt; — catch exceptions gracefully&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent execution&lt;/strong&gt;: &lt;code&gt;-&amp;gt;concurrent()&lt;/code&gt; — tools run in parallel via &lt;code&gt;pcntl_fork&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decorators&lt;/strong&gt;: wrap tools with logging, caching, or any custom behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool choice&lt;/strong&gt;: &lt;code&gt;-&amp;gt;withToolChoice(Provider::REQ)&lt;/code&gt; to force tool use&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Provider tools
&lt;/h4&gt;

&lt;p&gt;Some providers offer built-in server-side tools like web search and code execution. Prisma normalizes these too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aimeos\Prisma\Tools&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'anthropic'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withTools&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'web_search'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'code_execution'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Search for the latest PHP version and verify it'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Available provider tools include &lt;code&gt;web_search&lt;/code&gt;, &lt;code&gt;code_execution&lt;/code&gt;, &lt;code&gt;file_search&lt;/code&gt;, &lt;code&gt;web_fetch&lt;/code&gt;, &lt;code&gt;image_generation&lt;/code&gt;, and &lt;code&gt;document_library&lt;/code&gt; — each mapped to the providers that support them. You can mix custom and provider tools in the same request.&lt;/p&gt;

&lt;h4&gt;
  
  
  Framework tool adapters
&lt;/h4&gt;

&lt;p&gt;Already have tools built for Laravel AI or Symfony? Reuse them directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Laravel AI / Prism tools&lt;/span&gt;
&lt;span class="nv"&gt;$tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;laravel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyLaravelTool&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Symfony #[AsTool] classes&lt;/span&gt;
&lt;span class="nv"&gt;$tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;symfony&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MySymfonyTool&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Thinking budgets
&lt;/h3&gt;

&lt;p&gt;Enable extended reasoning for models that support it. Prisma automatically maps token counts to each provider's native format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'anthropic'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withThinkingBudget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withMaxTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Solve this step by step: ...'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Access the model's reasoning&lt;/span&gt;
&lt;span class="nv"&gt;$thinking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;'thinking'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Citations
&lt;/h3&gt;

&lt;p&gt;Normalized citation objects across providers that support them (Anthropic, OpenAI, Gemini, Perplexity, xAI):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;citations&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$citation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$citation&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// source title&lt;/span&gt;
    &lt;span class="nv"&gt;$citation&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="c1"&gt;// source URL&lt;/span&gt;
    &lt;span class="nv"&gt;$citation&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// output text that cites this source&lt;/span&gt;
    &lt;span class="nv"&gt;$citation&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;source&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// verbatim quote from the source&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rate limits and retry handling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Automatic retry with exponential backoff&lt;/span&gt;
&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'openai'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withClientRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$attempt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Inspect rate limit headers&lt;/span&gt;
&lt;span class="nv"&gt;$limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;rateLimit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$limit&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// requests left&lt;/span&gt;
&lt;span class="nv"&gt;$limit&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;retryAfter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// seconds until reset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provider coverage
&lt;/h2&gt;

&lt;p&gt;Here's what's supported for text providers in 0.4:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;structure&lt;/th&gt;
&lt;th&gt;translate&lt;/th&gt;
&lt;th&gt;write&lt;/th&gt;
&lt;th&gt;citations&lt;/th&gt;
&lt;th&gt;custom tools&lt;/th&gt;
&lt;th&gt;provider tools&lt;/th&gt;
&lt;th&gt;thinking budget&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alibaba&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Anthropic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bedrock&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cohere&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deepseek&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DeepL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gemini&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Groq&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mistral&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ollama&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;beta&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;beta&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenAI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Openrouter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Perplexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;beta&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;beta&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;xAI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;beta&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;beta&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And that's just text. Prisma also covers &lt;strong&gt;9 audio providers&lt;/strong&gt; (speak, transcribe, demix, denoise, revoice, describe), &lt;strong&gt;14 image providers&lt;/strong&gt; (imagine, inpaint, upscale, vectorize, background, erase, and more), and &lt;strong&gt;video description&lt;/strong&gt; via Gemini.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require aimeos/prisma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Minimal example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aimeos\Prisma\Prisma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Text&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'openai'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'OPENAI_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'What makes PHP great?'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Image&lt;/span&gt;
&lt;span class="nv"&gt;$binary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'openai'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'api_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'OPENAI_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;imagine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a PHP elephant wearing sunglasses'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nb"&gt;file_put_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'elephant.png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$binary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No service providers, no config files, no framework dependencies. Just require and use.&lt;/p&gt;




&lt;p&gt;Prisma is MIT-licensed and open source. Check it out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href="https://php-prisma.org" rel="noopener noreferrer"&gt;https://php-prisma.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/aimeos/prisma" rel="noopener noreferrer"&gt;https://github.com/aimeos/prisma&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've been looking for a unified way to work with AI providers in PHP — especially beyond just text — give it a try. Feedback, issues, and stars are all welcome.&lt;/p&gt;

&lt;p&gt;In case you like Prisma, leave a star in the Github repo :-)&lt;/p&gt;

</description>
      <category>php</category>
      <category>ai</category>
      <category>laravel</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Aimeos: Laravel e-commerce 2026.04</title>
      <dc:creator>Aimeos</dc:creator>
      <pubDate>Tue, 12 May 2026 10:38:27 +0000</pubDate>
      <link>https://dev.to/aimeos/aimeos-laravel-e-commerce-202604-4oj3</link>
      <guid>https://dev.to/aimeos/aimeos-laravel-e-commerce-202604-4oj3</guid>
      <description>&lt;p&gt;If you're building e-commerce with Laravel, this one's for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new
&lt;/h2&gt;

&lt;p&gt;We just shipped Aimeos 2026.04 — here's what landed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Laravel 13 support
&lt;/h3&gt;

&lt;p&gt;The Laravel integration, the stand-alone shop, and the headless distribution all run on Laravel 13 out of the box. Upgrade and go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer CSV import
&lt;/h3&gt;

&lt;p&gt;Products, catalogs, and suppliers already had CSV import. Now customers do too. Full pipeline with address and property support, regex validation, group filtering, and an admin UI upload. The CSV import story is complete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product feed extension
&lt;/h3&gt;

&lt;p&gt;New extension for generating Google Merchant and Idealo product feeds. Drop it in, configure which products and details to export, done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security hardening
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;HTML sanitization in the CMS to prevent XSS&lt;/li&gt;
&lt;li&gt;Query depth and complexity limits on GraphQL&lt;/li&gt;
&lt;li&gt;Tighter permission checks in the admin API&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ready for PHP 9
&lt;/h3&gt;

&lt;p&gt;Minimum raised to PHP 8.1. Every deprecation removed across the core and 30+ extensions. Fully tested on PHP 8.5. PHPStan static analysis added at level 4 with zero errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wait, what's Aimeos?
&lt;/h2&gt;

&lt;p&gt;Aimeos is an open-source e-commerce framework (LGPLv3) that installs into Laravel as a Composer package. No separate shop system — you add e-commerce to your existing app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It works like Laravel, not against it.&lt;/strong&gt; Your routes, middleware, auth, queues, Blade views. You stay in Artisan and Eloquent. Aimeos plugs in rather than taking over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headless-first.&lt;/strong&gt; JSON:API and GraphQL APIs ship out of the box. Build your frontend in Vue, React, Livewire, Inertia — or use the included server-rendered HTML components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-tenant.&lt;/strong&gt; Run multiple shops from one Laravel install. Separate catalogs, pricing, languages, and currencies per site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It scales.&lt;/strong&gt; Same codebase for a single-product shop or a marketplace with millions of items. ElasticSearch and Solr integrations handle high-volume search.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extensible.&lt;/strong&gt; 30+ extensions for payments, shipping, CMS, feeds, Redis caching, search engines, and more. Custom extensions follow the same conventions without touching core code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No SaaS lock-in.&lt;/strong&gt; Self-hosted. You own your data. No per-transaction fees.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;One command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project aimeos/aimeos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aimeos.org/laravel-ecommerce-package" rel="noopener noreferrer"&gt;https://aimeos.org/laravel-ecommerce-package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aimeos/aimeos" rel="noopener noreferrer"&gt;https://github.com/aimeos/aimeos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aimeos.org/docs/laravel" rel="noopener noreferrer"&gt;https://aimeos.org/docs/laravel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://demo.aimeos.org" rel="noopener noreferrer"&gt;https://demo.aimeos.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you like Aimeos, give it a star on GitHub :-)&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>news</category>
      <category>opensource</category>
      <category>php</category>
    </item>
  </channel>
</rss>
