se7enxweb/ezplatform-site-api
Site API extension for eZ Platform/Ibexa that simplifies content access with named queries, reusable Site API objects, and a cleaner Twig/template layer. It helps developers build content-rich sites faster while preserving the platform's native content model and features.
First, calibrate your expectations: this is not a Laravel package. se7enxweb/ezplatform-site-api is a fork of Netgen's eZ Platform Site API — a Symfony bundle that wraps the eZ Platform / Ibexa v2-era public API (ezplatform-kernel) with a friendlier, read-optimised content model. There is no service provider, no config/site-api.php, no facade, and no Laravel discovery entry. Before writing any code, read composer.json in the tag:
require block: expect symfony/* constraints in the 3.4–4.4 range and ezsystems/ezplatform-kernel. Those constraints are the single biggest determinant of whether this can live in a Laravel app at all (Laravel 9+ ships Symfony 6 components).autoload.psr-4: the fork may or may not have re-namespaced classes. Confirm whether it still ships Netgen\EzPlatformSiteApi\ or a vendor-specific namespace, and whether it declares replace/provide for the upstream package. If it does not, you cannot install it alongside netgen/ezplatform-site-api without class collisions.version in the tag and whether 4.3.4 is a clean patch or carries an unmerged dependency bump.Minimal integration path:
composer require se7enxweb/ezplatform-site-api:4.3.4 into the Symfony/eZ app (or a standalone bootstrap), register the bundle in config/bundles.php, and ensure the kernel is booted with a valid siteaccess.Site service (the API entry point) into a controller or service — SiteAPI\Core\Site\Site exposes getContentService(), getFindService(), getLocationService(), getContentTypeService().lib/API/ (interfaces — this is the contract you code against) and lib/API/Values/ (Content, Location, ContentInfo, Field). Ignore the Core/ internals until you need to extend.mainLocation children. If that round-trips, the wiring is correct.Code against the API interfaces, not the Core classes. Type-hint SiteAPI\API\Site and SiteAPI\API\Values\Content. This is what makes the fork swap-able and your code testable.
final class LandingController
{
public function __construct(private \SiteAPI\API\Site $site) {}
public function __invoke(int $locationId): Response
{
$location = $this->site->getLocationService()->loadLocation($locationId);
$content = $location->content;
return $this->render('landing.html.twig', [
'title' => $content->getField('title')->value,
'children' => $location->children->locations,
]);
}
}
Prefer the Find/Filter services over the raw repository. getFindService()->findContent($query) / findLocations() and the filterContent() / filterLocations() variants are the day-to-day workhorses. The filter* methods operate on an already-loaded collection and avoid an extra search round-trip — use them for "children of the current location, narrowed by content type."
Traverse value objects rather than re-querying. Content gives you fields, getField(), mainLocation, contentInfo; Location gives you children, parent, siblings. Most N+1 problems in this ecosystem come from calling the load service inside a loop when the parent object already carried the data.
Use SiteAwareInterface for services that need the current siteaccess rather than injecting siteaccess strings around your codebase — the Site API resolves the active site for you, and that matters on multi-site installs.
In Twig, lean on the bundle's functions (ez_render_field, the Site API content/location rendering helpers) instead of hand-assembling field markup. If you inherit templates from a standard eZ install, keep the upstream view configuration and override per content type.
If you genuinely need this in Laravel, do not try to boot the Symfony kernel inside a Laravel request. Treat it as an out-of-process boundary: a small Symfony/console application that exposes content over HTTP or a queue, called from a Laravel HTTP client or job. Share only DTOs. Any in-process bridge means reconciling two container configurations and two siteaccess resolvers — it will cost more than it saves.
Pin exactly and test the upgrade. Use "se7enxweb/ezplatform-site-api": "4.3.4" (no caret) in the fork era, and put the package's own test suite plus one smoke test of your most complex query in CI.
4.3.3...v4.3.4 locally (git diff --stat, then read every changed file under lib/API/ and lib/Core/Site/) before bumping. Patches in forks routinely carry silent signature changes.replace clause, installing this next to netgen/ezplatform-site-api yields duplicate PSR-4 roots and confusing "class not found / already declared" errors. composer why netgen/ezplatform-site-api first.getField() throws on a missing field identifier. Guard with $content->hasField('x') or use the fields collection. Also check $field->isEmpty() — an empty field returns a Field object with a null-ish value, not null.getFieldRelation() / relation fields return content or location objects and can trigger lazy loads. Eager-loading is your responsibility; there is no with().findContent() returns a SearchResult, not an array. Iterate ->searchHits (each hit wraps ->valueObject) and read ->totalCount for pagination. Treating it as an iterable of Content objects is the most common first-day bug.debug:container on the Site service and log the resolved site.X-Location-Id values leak wrong content into shared caches. Test with the cache enabled, not just in dev.bin/console debug:container SiteAPI to see the wired services, debug:config for the bundle's tree, and the Symfony profiler's query panel for search round-trips. When a query returns nothing, dump the generated ContentQuery before touching your template.API\* interfaces in your own namespace and alias them in services.yaml rather than subclassing Core\Site\* classes — the Core layer changes between patch releases more often than the API does.How can I help you explore Laravel packages today?