<?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>PagibleAI: The Best Headless CMS for Laravel</title>
      <dc:creator>Aimeos</dc:creator>
      <pubDate>Thu, 16 Jul 2026 11:23:33 +0000</pubDate>
      <link>https://dev.to/aimeos/pagibleai-the-best-headless-cms-for-laravel-4koo</link>
      <guid>https://dev.to/aimeos/pagibleai-the-best-headless-cms-for-laravel-4koo</guid>
      <description>&lt;p&gt;Headless is supposed to loosen the frontend. Too often, it merely moves the constraint somewhere else.&lt;/p&gt;

&lt;p&gt;A hosted platform places content behind a remote service. A toolkit leaves the delivery API and publishing model to the application. An admin framework can turn a headless project into a CMS development project before the frontend has even begun.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pagible.com" rel="noopener noreferrer"&gt;PagibleAI CMS&lt;/a&gt; keeps the content system inside Laravel, exposes published material through a standard API and leaves the frontend entirely open.&lt;/p&gt;

&lt;p&gt;Structured content, reusable elements, media, drafts, immutable versions and scheduled publishing are already part of the package.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CMS remains part of the application
&lt;/h2&gt;

&lt;p&gt;PagibleAI installs into an existing Laravel project:&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;The CMS does not live behind a vendor account or inside a second application with its own users, permissions and release cycle. It shares the Laravel project’s database, authentication, storage, cache and deployment process.&lt;/p&gt;

&lt;p&gt;The frontend is decoupled; deployment and operations remain consolidated.&lt;/p&gt;

&lt;p&gt;The useful boundary in a headless system lies between content and presentation, not between a Laravel application and its own content.&lt;/p&gt;

&lt;p&gt;A remote CMS can be the right decision when outsourcing infrastructure is the priority. It is less persuasive when the project already has a capable application platform and the additional service creates another place where business rules must be translated, synchronized and monitored.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the package already settles
&lt;/h2&gt;

&lt;p&gt;Several decisions precede the first frontend request. PagibleAI makes them explicit:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;PagibleAI’s answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Public delivery&lt;/td&gt;
&lt;td&gt;Read-only JSON:API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editorial operations&lt;/td&gt;
&lt;td&gt;Administrative GraphQL API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content model&lt;/td&gt;
&lt;td&gt;Pages, reusable elements, files and versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Publishing&lt;/td&gt;
&lt;td&gt;Drafts, scheduling, history and restoration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Site structure&lt;/td&gt;
&lt;td&gt;Hierarchical page trees for navigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Any API client, with Blade available when useful&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reach&lt;/td&gt;
&lt;td&gt;Multiple domains and languages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;Full-text search through Laravel Scout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ownership&lt;/td&gt;
&lt;td&gt;Open source and self-hosted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automation&lt;/td&gt;
&lt;td&gt;Optional AI tools and an MCP interface&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PagibleAI documents this separation between editorial operations and public delivery in the &lt;a href="https://pagible.com/headless-cms" rel="noopener noreferrer"&gt;PagibleAI headless CMS guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Editors manage versions and reusable content through one publishing system, while frontends receive a stable stream of published material. You extend the CMS instead of finishing its foundations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Laravel alternatives
&lt;/h2&gt;

&lt;p&gt;The Laravel shortlist usually includes Statamic, Twill and Filament. The practical difference is how much work remains after installation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Best suited to&lt;/th&gt;
&lt;th&gt;What remains&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PagibleAI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Most headless Laravel applications&lt;/td&gt;
&lt;td&gt;All included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Statamic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams wanting a mature commercial CMS and paid support&lt;/td&gt;
&lt;td&gt;A Pro license for headless features&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Twill&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bespoke editorial systems with custom models&lt;/td&gt;
&lt;td&gt;The public delivery API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Filament&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom back offices and administration tools&lt;/td&gt;
&lt;td&gt;The CMS itself&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Statamic offers a control panel, REST and GraphQL, but requires a paid &lt;a href="https://statamic.com/pricing?lang=en" rel="noopener noreferrer"&gt;Statamic Pro&lt;/a&gt; license for headless delivery and multi-user publishing.&lt;/p&gt;

&lt;p&gt;Twill supplies editorial components and flexible content modules, while leaving &lt;a href="https://twillcms.com/docs/getting-started/installation.html" rel="noopener noreferrer"&gt;frontend routes and API endpoints to the developer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Filament provides &lt;a href="https://filamentphp.com/docs/5.x/getting-started" rel="noopener noreferrer"&gt;CRUD interfaces for Eloquent models&lt;/a&gt;; publication states, versions, previews and content delivery remain your own concerns.&lt;/p&gt;

&lt;p&gt;PagibleAI is the only option in this group that combines a complete open-source CMS with a built-in public delivery API - without a paid headless tier or a custom delivery layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  A deliberate boundary between editing and delivery
&lt;/h2&gt;

&lt;p&gt;The public and editorial APIs have different roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GraphQL serves the administration layer, where editors and custom tools need precise queries, relationships and mutations&lt;/li&gt;
&lt;li&gt;Public frontends receive published content through a high performance read-only JSON API following the JSON:API standard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The delivery API has a narrow promise: return content that is ready to be shown. A frontend does not need to interpret editorial permissions or infer whether a record is still a draft. The public contract stays read-only and editorial complexity remains behind GraphQL.&lt;/p&gt;

&lt;p&gt;The choice of frontend belongs to your project. Nuxt, React, VueJS, a native application or another service can consume the same published content. If parts of the site is better served directly by Laravel, Blade remains available without requiring a second content system.&lt;/p&gt;

&lt;p&gt;Headless delivery becomes a choice of presentation, not a demand to split every project into separate applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content that survives the current frontend
&lt;/h2&gt;

&lt;p&gt;A headless CMS should preserve structure without smuggling presentation into the content.&lt;/p&gt;

&lt;p&gt;PagibleAI separates pages, reusable elements, files and versions. A product statement, author profile, legal notice or call to action can be maintained once and placed wherever it belongs. Each frontend decides how to present it.&lt;/p&gt;

&lt;p&gt;An editor can update a product statement once and publish it. The same approved version then becomes available to a Nuxt storefront, a mobile application and a customer portal, while each frontend controls its presentation.&lt;/p&gt;

&lt;p&gt;The page tree supplies hierarchy for navigation, while shared elements remain reusable beyond an individual page. Editors retain a coherent view of the site without binding its content to one template system.&lt;/p&gt;

&lt;p&gt;That separation also governs publishing. Every editorial save creates a snapshot. Editors work with the latest version; public consumers continue to receive the published one. A replacement can be reviewed, scheduled or discarded without disturbing the live experience.&lt;/p&gt;

&lt;p&gt;When several frontends depend on the same CMS, this separation becomes essential. Publication is decided once, at the source, rather than interpreted independently by every consumer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unglamorous work is already done
&lt;/h2&gt;

&lt;p&gt;The attraction of building a CMS from smaller Laravel components is strongest at the beginning. A model, migration and administration form can be produced quickly.&lt;/p&gt;

&lt;p&gt;The expensive work comes later: search, restoration, media relationships, translations, page movement, safe previews, publication scheduling and API compatibility.&lt;/p&gt;

&lt;p&gt;PagibleAI treats those concerns as part of the product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full-text search works with SQLite, MySQL, PostgreSQL and SQL Server&lt;/li&gt;
&lt;li&gt;Multiple domains and languages can share one installation&lt;/li&gt;
&lt;li&gt;Tenant-scoped content supports projects operating several sites from the same Laravel deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these capabilities is especially fashionable but all of them become expensive when discovered late.&lt;/p&gt;

&lt;p&gt;PagibleAI also includes optional AI-assisted editing and a full MCP interface, but neither is required for the headless publishing workflow.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://pagible.com/features" rel="noopener noreferrer"&gt;PagibleAI feature overview&lt;/a&gt; documents these capabilities in greater detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where PagibleAI may not be the answer
&lt;/h2&gt;

&lt;p&gt;It is self-hosted and your team operates it as part of the Laravel application. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the primary requirement is to hand the entire CMS operation to a vendor, a managed platform is the more natural choice.&lt;/li&gt;
&lt;li&gt;Public delivery is centred on JSON:API for performance and scalability reasons, while GraphQL serves editorial work. A project committed specifically to a public GraphQL content API would need to add that layer.&lt;/li&gt;
&lt;li&gt;Multi-tenancy uses a shared database. Applications requiring a separate database for every tenant fall outside the intended model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most Laravel teams seeking a self-hosted headless CMS, the trade is still favourable: the content and publishing system arrives complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better use of development time
&lt;/h2&gt;

&lt;p&gt;The real cost of a headless CMS is the engineering it diverts from the product.&lt;/p&gt;

&lt;p&gt;PagibleAI handles content structure, editorial workflow and publication state. Your team shapes the frontend, its design, interactions, performance and relationship with the rest of the application.&lt;/p&gt;

&lt;p&gt;Editors get a dependable publishing environment, frontends consume consistent published content, and developers decide how that content becomes a product. The CMS stays close to Laravel without tying the presentation to it.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/aimeos/pagible" rel="noopener noreferrer"&gt;PagibleAI source code&lt;/a&gt; is available on GitHub.&lt;/p&gt;

&lt;p&gt;For a Laravel team that wants a headless CMS without also building one, PagibleAI is the strongest option.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>headlesscms</category>
      <category>php</category>
      <category>ai</category>
    </item>
    <item>
      <title>Aimeos: The Headless E-Commerce Framework for Laravel</title>
      <dc:creator>Aimeos</dc:creator>
      <pubDate>Sat, 04 Jul 2026 16:58:52 +0000</pubDate>
      <link>https://dev.to/aimeos/aimeos-the-headless-e-commerce-framework-for-laravel-232j</link>
      <guid>https://dev.to/aimeos/aimeos-the-headless-e-commerce-framework-for-laravel-232j</guid>
      <description>&lt;p&gt;Laravel developers rarely need help building a storefront. Given a familiar stack, they can ship one with Blade, Livewire, Vue, React, Next.js, Nuxt, or a mobile client without much ceremony. The harder question is what happens after the first version is live, when the business starts asking for regional catalogs, customer-specific prices, subscriptions, stock rules, marketplace workflows, imports, exports, payment integrations, and an admin experience that non-developers can use every day.&lt;/p&gt;

&lt;p&gt;That is the point where many lightweight e-commerce packages begin to show their limits. They are pleasant while the model is simple, but the custom code around them grows quickly once the shop becomes central to the business, which is why Aimeos should be the default starting point when e-commerce is expected to become a serious part of a Laravel application rather than a small feature beside it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://aimeos.org/laravel-headless-ecommerce" rel="noopener noreferrer"&gt;Aimeos&lt;/a&gt; approaches the problem from the stronger direction: it gives Laravel a mature e-commerce engine first, then leaves the presentation layer open.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Aimeos is not just a cart, a product table, and a checkout flow. It brings catalog management, basket handling, checkout, orders, customers, stock, suppliers, services, subscriptions, scheduled jobs, imports, a full administration interface, a JSON REST API for storefronts, and a GraphQL API for administration. More importantly, it does all of this without forcing Laravel to disappear behind a separate platform.&lt;/p&gt;

&lt;p&gt;That makes Aimeos the clearer favorite for ambitious Laravel e-commerce projects. Lunar PHP and Laravel Shopper both have useful places in the ecosystem, but Aimeos is the one that starts with the broader e-commerce model, the operational admin, the APIs, and the growth path already in place.&lt;/p&gt;

&lt;p&gt;To install the Aimeos headless distribution, use:&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-headless aimeos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Short Version
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Laravel&lt;/td&gt;
&lt;td&gt;Application structure, routing, authentication, sessions, cache, logging, mail, queues, policies, and deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aimeos&lt;/td&gt;
&lt;td&gt;Catalog, prices, stock, basket, checkout, orders, services, subscriptions, jobs, imports, APIs, and administration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storefront&lt;/td&gt;
&lt;td&gt;Blade, Livewire, Vue, React, Next.js, Nuxt, mobile apps, partner portals, or any other client&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That division is the real argument for choosing Aimeos first. Laravel remains the application, Aimeos becomes the e-commerce engine, and the frontend can evolve without turning every business rule into custom application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Laravel Remains The Application, Not The Shopping Cart
&lt;/h2&gt;

&lt;p&gt;The best part of Aimeos in a Laravel project is not only the feature set, but the boundary it draws. Laravel continues to own the application structure: routing, authentication, sessions, cache, logging, configuration, mail, queues, policies, and deployment. Aimeos takes responsibility for the e-commerce domain, where product data, prices, baskets, stock, orders, services, and subscriptions need a model that has already survived real complexity.&lt;/p&gt;

&lt;p&gt;That separation is what makes Aimeos especially suitable for headless e-commerce. The storefront can change without rewriting the engine beneath it, while the e-commerce layer remains deep enough for the business requirements that usually arrive later. A team can start with a conventional Laravel storefront and still have a path toward a PWA, a mobile app, a B2B portal, a marketplace frontend, or partner-facing API clients.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laravel application
    -&amp;gt; routing, auth, cache, sessions, queues, mail, policies

Aimeos e-commerce engine
    -&amp;gt; catalog, prices, stock, basket, checkout, orders, admin

APIs
    -&amp;gt; JSON:API for storefronts
    -&amp;gt; GraphQL for administration

Clients
    -&amp;gt; Blade, Livewire, Vue, React, Next.js, Nuxt, mobile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may sound simple, but it is exactly the division many e-commerce projects fail to protect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headless Without An Unfinished Back Office
&lt;/h2&gt;

&lt;p&gt;Headless e-commerce is often described as frontend freedom, although that framing leaves out the part that matters most in production. A custom storefront is only useful if the people running the business can still manage products, prices, orders, content, stock, and customer data without waiting for developers to edit database rows or build internal tools from scratch.&lt;/p&gt;

&lt;p&gt;Aimeos avoids that trap because its headless APIs sit beside an existing administration system rather than replacing it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aimeos part&lt;/th&gt;
&lt;th&gt;Why it matters in a headless setup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JSON REST API&lt;/td&gt;
&lt;td&gt;Custom storefronts can work against the e-commerce engine without being tied to one frontend stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GraphQL admin API&lt;/td&gt;
&lt;td&gt;Administration and automation can access shop data through a structured API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard admin&lt;/td&gt;
&lt;td&gt;Operations teams still have a usable interface for daily work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jobs and imports&lt;/td&gt;
&lt;td&gt;Product data, stock updates, and scheduled processes do not have to be invented around the edges&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That balance is one of the main reasons Aimeos feels different from thinner Laravel e-commerce packages. It treats the frontend as replaceable, but it does not treat the e-commerce operation as an exercise left to the application team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built For The Moment The Shop Becomes A Platform
&lt;/h2&gt;

&lt;p&gt;A simple store can survive on a simple package, and there is nothing wrong with that. The reason to choose Aimeos is that many Laravel e-commerce projects do not stay simple for very long. A brand expands into multiple regions, a B2C shop gains B2B accounts, one catalog becomes several catalogs, a single seller model becomes a marketplace, or the product data grows large enough that imports, indexing, and caching become architectural concerns rather than background tasks.&lt;/p&gt;

&lt;p&gt;That is where Aimeos starts to pay for its additional depth:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multi-site setups where one installation serves several channels or regions&lt;/li&gt;
&lt;li&gt;B2B projects with customer-specific prices, visibility, or ordering behavior&lt;/li&gt;
&lt;li&gt;marketplace models where vendors, products, orders, and payments need clearer boundaries&lt;/li&gt;
&lt;li&gt;subscription-oriented projects where recurring processes must be part of the e-commerce model&lt;/li&gt;
&lt;li&gt;large catalogs where indexing, imports, caching, and operational tooling matter early&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aimeos is built for those situations. Its multi-site capabilities, broad e-commerce model, marketplace and B2B orientation, subscription support, import and job infrastructure, and scaling options make it the stronger foundation for projects where e-commerce is not an add-on feature but the system the business depends on.&lt;/p&gt;

&lt;p&gt;The tradeoff is that Aimeos has more concepts to learn than a smaller package. That is not a weakness if the project is expected to grow, because the complexity exists either way; the choice is whether it lives in a framework designed for e-commerce or in custom application code that accumulates around a package that was never meant to carry that much responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Aimeos Comes Out Ahead Of Lunar PHP And Laravel Shopper
&lt;/h2&gt;

&lt;p&gt;Lunar PHP and Laravel Shopper are both useful Laravel e-commerce projects, but they are narrower choices. The practical question is not whether they can work, because they can, but whether they give a Laravel team enough e-commerce depth before the business starts asking for more complex catalogs, B2B rules, marketplace behavior, subscriptions, imports, operational tooling, and multiple storefronts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Best fit&lt;/th&gt;
&lt;th&gt;What stands out&lt;/th&gt;
&lt;th&gt;Why Aimeos is favored&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Aimeos&lt;/td&gt;
&lt;td&gt;Larger e-commerce platforms, B2B, marketplaces, multi-site setups, complex catalogs, and projects expected to scale&lt;/td&gt;
&lt;td&gt;A mature e-commerce engine with APIs, admin, jobs, imports, subscriptions, and operational depth&lt;/td&gt;
&lt;td&gt;It is the strongest default when e-commerce is core to the business&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lunar PHP&lt;/td&gt;
&lt;td&gt;Custom Laravel stores where an Eloquent-first development style is the priority&lt;/td&gt;
&lt;td&gt;A familiar Laravel feel with clean developer experience&lt;/td&gt;
&lt;td&gt;Aimeos gives more platform depth when requirements move beyond a custom store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laravel Shopper&lt;/td&gt;
&lt;td&gt;Existing Laravel apps that need package-style e-commerce building blocks&lt;/td&gt;
&lt;td&gt;A Laravel-friendly, Filament-based approach with swappable models and composable packages&lt;/td&gt;
&lt;td&gt;Aimeos is stronger when the project needs a complete e-commerce engine instead of building blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Lunar PHP is attractive when the development team wants the e-commerce layer to feel as close to Laravel conventions as possible. Its Eloquent-first model and Laravel-native style make sense for teams that want a custom store and expect to shape more of the surrounding business logic themselves, although that strength becomes a limitation when the project needs broader platform behavior rather than a Laravel-shaped e-commerce package.&lt;/p&gt;

&lt;p&gt;Laravel Shopper is appealing from a different angle. It is package-oriented, uses familiar Laravel ecosystem tools, and fits teams that want to add e-commerce capabilities to an existing application without adopting a heavier e-commerce platform, although Aimeos is the stronger option when the e-commerce layer should become durable infrastructure instead of a set of adaptable building blocks.&lt;/p&gt;

&lt;p&gt;Aimeos is the clearer favorite when the e-commerce system itself needs depth from the beginning. If the roadmap includes B2B rules, multiple storefronts, marketplace behavior, subscriptions, large imports, operational administration, or very large catalogs, Aimeos gives the Laravel team more of the foundation upfront rather than asking them to build it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Practical Case For Aimeos
&lt;/h2&gt;

&lt;p&gt;The strongest argument for Aimeos is not simply that it is headless, because many tools can make that claim. The stronger argument is that Aimeos combines headless flexibility with a serious e-commerce core, a working admin, Laravel integration, and enough architectural room for projects that become more demanding over time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Choose Aimeos when the shop is expected to become a platform,
and treat smaller packages as niche choices for simpler stores.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That combination is difficult to replace. A thin package may feel faster at the beginning, while a hosted platform may remove too much control from a Laravel team that wants to own its application. Aimeos occupies the strongest middle ground: it gives Laravel developers a real e-commerce framework without taking away the freedom to build the customer experience in the stack that suits the product.&lt;/p&gt;

&lt;p&gt;For small stores, Aimeos may be more power than necessary. For ambitious Laravel e-commerce projects, especially those likely to move into B2B, marketplace, multi-site, subscription, or high-volume catalog territory, that power is exactly the reason Aimeos should be the preferred choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aimeos.org/laravel-headless-ecommerce" rel="noopener noreferrer"&gt;Aimeos headless&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aimeos/aimeos-headless" rel="noopener noreferrer"&gt;Github repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aimeos.org/docs" rel="noopener noreferrer"&gt;Aimeos documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>laravel</category>
      <category>ecommerce</category>
      <category>php</category>
      <category>headless</category>
    </item>
    <item>
      <title>Aimeos Prisma 0.5: a sturdier PHP layer for AI providers</title>
      <dc:creator>Aimeos</dc:creator>
      <pubDate>Tue, 30 Jun 2026 10:11:09 +0000</pubDate>
      <link>https://dev.to/aimeos/aimeos-prisma-05-a-sturdier-php-layer-for-ai-providers-n7i</link>
      <guid>https://dev.to/aimeos/aimeos-prisma-05-a-sturdier-php-layer-for-ai-providers-n7i</guid>
      <description>&lt;p&gt;Most AI integrations do not fail because the first provider is hard to call. They become awkward later, when the application has to speak several dialects at once.&lt;/p&gt;

&lt;p&gt;One endpoint generates text. Another returns embeddings. A third handles transcription. Image generation arrives with its own payload shape, its own errors, and its own idea of metadata. Add web search, tool calling, local models, or an OpenAI-compatible gateway, and the useful application code starts to disappear behind provider plumbing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aimeos/prisma" rel="noopener noreferrer"&gt;Aimeos Prisma&lt;/a&gt; exists to keep that boundary small. It gives PHP applications one API across text, image, audio, and video providers, while still leaving room for provider-specific options when they are needed.&lt;/p&gt;

&lt;p&gt;The 0.5 release makes that layer more suitable for real applications: streamed responses, conversation history, stronger schema support, request observation, better test doubles, safer response handling, and more provider coverage.&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;
  
  
  The API shape
&lt;/h2&gt;

&lt;p&gt;Prisma is a framework-agnostic PHP 8.2+ package. It is MIT-licensed, uses Guzzle internally, and keeps the calling pattern consistent across provider types.&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="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="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;'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="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="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'STABILITYAI_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 clean product photo of a ceramic coffee cup'&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="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="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DEEPGRAM_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;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="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="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GEMINI_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;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;The provider name changes. The surrounding application shape does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaming belongs in the core API
&lt;/h2&gt;

&lt;p&gt;For user-facing tools, waiting for a full model response is often the wrong interaction. Prisma 0.5 adds &lt;code&gt;stream()&lt;/code&gt; as a text capability instead of treating it as a separate integration path.&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="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="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;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stream'&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;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Write a short launch announcement for our PHP package'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;stream&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;$delta&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;$delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$full&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;output&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$usage&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;usage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Streaming keeps the same configuration surface as &lt;code&gt;write()&lt;/code&gt;: model selection, system prompts, tools, prior messages, and provider options all travel through the same provider object.&lt;/p&gt;

&lt;p&gt;In Laravel, it can be handed directly to &lt;code&gt;response()-&amp;gt;eventStream()&lt;/code&gt;:&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;Illuminate\Support\Facades\Route&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/chat'&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="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="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'services.openai'&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;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stream'&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;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Explain event sourcing in plain English'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&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;eventStream&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="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&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;stream&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;$chunk&lt;/span&gt;&lt;span class="p"&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="nb"&gt;is_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$chunk&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nv"&gt;$chunk&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="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;There is one operational rule: consume the stream before reading body-derived metadata. Usage, citations, tool steps, finish reason, and response metadata are complete only after the stream has been drained. Rate-limit data from headers is available immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conversation history without a second abstraction
&lt;/h2&gt;

&lt;p&gt;Chat history now lives in &lt;code&gt;withMessages()&lt;/code&gt;. The current prompt passed to &lt;code&gt;write()&lt;/code&gt;, &lt;code&gt;stream()&lt;/code&gt;, or &lt;code&gt;structure()&lt;/code&gt; becomes the next user turn.&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="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="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ANTHROPIC_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;withSystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'You are a concise technical 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;withMessages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'I need a laptop recommendation.'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'assistant'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'What is your budget and workload?'&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;'Around 1500 EUR, mostly PHP development and light video editing.'&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;p&gt;For multimodal providers, user turns can also carry files, subject to each provider's file support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured output is stricter when it can be, explicit when it cannot
&lt;/h2&gt;

&lt;p&gt;Structured output is most useful when the code can state what it expects and then check what it received. Prisma 0.5 improves both sides.&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="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;'ticket'&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;'priority'&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;'low'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'normal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'high'&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;'tags'&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="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="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;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'structure'&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;'Extract a support ticket from: Checkout is broken for EU cards'&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;$ticket&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, Prisma uses the provider's native structured-output mode when one exists. If a schema is too large or deeply nested for that mode, pass &lt;code&gt;['mode' =&amp;gt; 'json']&lt;/code&gt; and Prisma will embed the schema in the prompt and parse the returned JSON.&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;'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;structure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Extract a support ticket'&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="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'mode'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'json'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The schema builder also gained unions, reusable definitions, references, and validation:&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;'order'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'billing'&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;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Address'&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;'shipping'&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;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Address'&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;'discount'&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;anyOf&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="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="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;def&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Address'&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;object&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'street'&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;'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="p"&gt;]));&lt;/span&gt;

&lt;span class="nv"&gt;$errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$schema&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&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;structured&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;$errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// reject, retry, or ask for a correction&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provider-enforced structured output reduces risk, but it does not make generated content trusted. Validate the shape you receive, and handle generated values as carefully as any user-supplied input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wider provider coverage
&lt;/h2&gt;

&lt;p&gt;Prisma 0.5 now covers 29 providers across text, image, audio, and video workflows. The release adds Azure OpenAI text support, VertexAI text support, image generation through xAI, ModelsLab, and Replicate, and text embeddings across more providers.&lt;/p&gt;

&lt;p&gt;OpenAI-compatible endpoints remain a practical escape hatch. Local servers, internal gateways, and proxy services can be used through the &lt;code&gt;openai&lt;/code&gt; provider by changing the base 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="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="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GATEWAY_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://my-gateway.example.com'&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;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'my-model'&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;'Hello from an OpenAI-compatible gateway'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provider tools also behave more carefully in 0.5. Prisma can omit unsupported combinations, such as a server-side tool that a provider cannot use for the current request shape, without forcing every application to know those edge cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability where the call happens
&lt;/h2&gt;

&lt;p&gt;Production AI calls need accounting. A plain response string cannot tell you which model ran, how long the request took, whether it failed, or what usage the provider reported.&lt;/p&gt;

&lt;p&gt;Prisma 0.5 adds request-scoped observers:&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\Values\Observation&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;observe&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;Observation&lt;/span&gt; &lt;span class="nv"&gt;$observation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;logger&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;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ai.operation'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$observation&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toArray&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;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;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gpt-4.1-mini'&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;'Draft a changelog entry'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An observation records the operation, provider type, provider name, model, duration, error state, usage, and metadata. Streamed responses are observed after the stream completes, so their usage and metadata are present.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage and metadata now have names
&lt;/h2&gt;

&lt;p&gt;Provider usage payloads are inconsistent. &lt;code&gt;input_tokens&lt;/code&gt;, &lt;code&gt;prompt_tokens&lt;/code&gt;, &lt;code&gt;promptTokenCount&lt;/code&gt;, and &lt;code&gt;inputTokens&lt;/code&gt; may all mean the same thing, depending on which API answered.&lt;/p&gt;

&lt;p&gt;In 0.5, &lt;code&gt;usage()&lt;/code&gt; returns a &lt;code&gt;Values\Usage&lt;/code&gt; object:&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;$usage&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;usage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;promptTokens&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;completionTokens&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;totalTokens&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheReadTokens&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheWriteTokens&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;thoughtTokens&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;used&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;meta()&lt;/code&gt; returns a &lt;code&gt;Values\Meta&lt;/code&gt; object:&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;$meta&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="nv"&gt;$meta&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$meta&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$meta&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;thinking&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$meta&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;reasoningDetails&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both objects remain array-compatible for provider-specific fields:&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;$rawUsage&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;usage&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;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$created&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;'created'&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;p&gt;Existing code that type-hints these results as arrays should switch to &lt;code&gt;Values\Usage&lt;/code&gt;, &lt;code&gt;Values\Meta&lt;/code&gt;, or call &lt;code&gt;all()&lt;/code&gt; when a raw array is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing without a live provider
&lt;/h2&gt;

&lt;p&gt;Application tests should not need live API keys to prove that the right Prisma calls are made. Prisma 0.5 adds a recording fake:&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\Responses\TextResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$fake&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;fake&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;TextResponse&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Invoice approved'&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;withUsage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'prompt_tokens'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'completion_tokens'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$result&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;'test'&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;'Review this invoice'&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="nv"&gt;$fake&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertCalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'write'&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str_contains&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'invoice'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nc"&gt;Prisma&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For provider-level tests, the new testing helpers can run real provider code against mocked Guzzle responses. Request building, response parsing, streaming, and tool loops can be exercised without leaving the test process.&lt;/p&gt;

&lt;p&gt;Responses implement &lt;code&gt;JsonSerializable&lt;/code&gt;, which keeps snapshot-style assertions straightforward:&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;$payload&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;jsonSerialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Safer response boundaries
&lt;/h2&gt;

&lt;p&gt;Provider responses may be streamed, proxied, malformed, or simply too large. &lt;code&gt;withMaxResponseSize()&lt;/code&gt; bounds how many bytes Prisma will read from a single provider response:&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;'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;withMaxResponseSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&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;'Analyze this document'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default limit is 64 MB per provider response. It applies to streamed and non-streamed calls, including individual tool-loop turns.&lt;/p&gt;

&lt;p&gt;The release also tightens provider discovery by validating provider type and name before building provider class names.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom providers are part of the story
&lt;/h2&gt;

&lt;p&gt;Prisma resolves providers by namespace convention:&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="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;'myprovider'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That maps to:&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="nc"&gt;Aimeos\Prisma\Providers\Text\Myprovider&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new &lt;a href="https://github.com/aimeos/prisma/blob/master/CUSTOM-PROVIDERS.md" rel="noopener noreferrer"&gt;custom provider guide&lt;/a&gt; documents provider discovery, contracts, response objects, structured output, tool loops, async operations, OpenAI-compatible APIs, and testing.&lt;/p&gt;

&lt;p&gt;That is useful when a project has an internal gateway, a private model host, or a provider that Prisma does not ship yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrading
&lt;/h2&gt;

&lt;p&gt;The main migration points are small but worth checking. Use &lt;code&gt;stream()&lt;/code&gt; for live text responses. Pass prior turns through &lt;code&gt;withMessages()&lt;/code&gt;. Call &lt;code&gt;structure()&lt;/code&gt; to request structured output; keep using &lt;code&gt;structured()&lt;/code&gt; to read the parsed response. Treat &lt;code&gt;usage()&lt;/code&gt; and &lt;code&gt;meta()&lt;/code&gt; as value objects, or call &lt;code&gt;all()&lt;/code&gt; when existing code expects arrays. Drain streamed responses before reading usage, metadata, citations, tool steps, or finish reason. Reset &lt;code&gt;Prisma::fake()&lt;/code&gt; in test teardown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;Docs: &lt;a href="https://php-prisma.org" rel="noopener noreferrer"&gt;https://php-prisma.org&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/aimeos/prisma" rel="noopener noreferrer"&gt;https://github.com/aimeos/prisma&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Custom providers: &lt;a href="https://github.com/aimeos/prisma/blob/master/CUSTOM-PROVIDERS.md" rel="noopener noreferrer"&gt;CUSTOM-PROVIDERS.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prisma 0.4 established the broad interface across text, image, audio, and video. Prisma 0.5 makes that interface easier to run in production: streamed UIs, observable calls, cheaper tests, clearer schemas, provider fallbacks, and custom integrations.&lt;/p&gt;

</description>
      <category>php</category>
      <category>ai</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <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>
