Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Core Laravel Package

ibexa/core

Ibexa Core is the foundation of the Ibexa Digital Experience Platform, delivering the content repository, public PHP APIs, and MVC layers needed to build and manage enterprise content applications on Symfony.

View on GitHub
Deep Wiki
Context7

Getting Started

ibexa/core is not a Laravel package and you should not composer require it into a Laravel app. It is the engine of Ibexa DXP: a Symfony 7.x bundle (Ibexa\Bundle\Core) plus the Public API contracts (Ibexa\Contracts\Core) and their implementations (Ibexa\Core). It arrives as a transitive dependency of the project skeleton, so the real "getting started" is:

composer create-project ibexa/oss my-project
cd my-project && composer require --dev ibexa/behat   # optional
php bin/console ibexa:install
php bin/console ibexa:reindex

Stack expectations for 5.x: PHP 8.3/8.4, Symfony 7.4 LTS, MySQL/PostgreSQL, and — for anything beyond basic filtering — Solr or Elasticsearch.

Where to look first, in this order:

  1. vendor/ibexa/core/src/contracts/Repository/ — the services you will inject every day: ContentService, LocationService, SearchService, ContentTypeService, UserService, LanguageService, URLService, FieldTypeService, plus Values/.
  2. vendor/ibexa/core/src/lib/ — implementations, FieldType/, Search/ (criterion visitors/handlers), Persistence/.
  3. vendor/ibexa/core/src/bundle/Core/Resources/config/ — how services are wired and tagged; this is your map for decoration and extension.

First use case — a controller that lists published articles through the Public API (PAPI). This is the shape 80% of your code will take:

use Ibexa\Contracts\Core\Repository\SearchService;
use Ibexa\Contracts\Core\Repository\Values\Content\Query;
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;

final class ArticleListController
{
    public function __construct(private readonly SearchService $searchService) {}

    public function __invoke(): Response
    {
        $query = new Query();
        $query->filter = new Criterion\LogicalAnd([
            new Criterion\ContentTypeIdentifier('article'),
            new Criterion\Visibility(Criterion\Visibility::VISIBLE),
        ]);
        $query->sortClauses = [new Query\SortClause\DatePublished(Query::SORT_DESC)];
        $query->limit = 20;

        $result = $this->searchService->findContent($query);

        return $this->render('articles.html.twig', ['items' => $result->searchHits]);
    }
}

From a Laravel mental model: PAPI is your Eloquent replacement (but read-mostly and service-oriented), Criterion is the query builder, Twig is Blade, and ibexa.system.<scope> config is your config/*.php.

Implementation Patterns

Inject contracts, never concrete classes. Type-hint Ibexa\Contracts\Core\Repository\ContentService etc. The root Repository object is only for beginTransaction() / commit() / rollback() and for getPermissionResolver(). Everything else goes through the granular services so decoration and test doubles keep working.

Writes: struct in, value out, transaction around it. Every mutation follows *CreateStruct → service call → publishVersion(). Field values must come from the field type's value struct, not arrays:

$contentType = $this->contentTypeService->loadContentTypeByIdentifier('article');
$create = $this->contentService->newContentCreateStruct($contentType, 'eng-GB');
$create->setField('title', 'Hello');
$create->setField('body', $bodyValue);   // from FieldTypeService->getFieldType('ezrichtext')->getValueStruct()
$draft = $this->contentService->createContent($create, [$locationCreateStruct]);
$content = $this->contentService->publishVersion($draft->versionInfo);

Wrap multi-object writes (content + relations + locations) in $repository->beginTransaction(); PAPI does not auto-wrap them.

Search: know filter vs. query, and metadata vs. content. $query->filter is cacheable and unscored; $query->query contributes score. Use findContentInfo() when you only need IDs/names, and location queries (LocationQuery) for tree/navigation work. Recent 5.x additions worth knowing: ContentTypeGroupName criterion for findContentTypes (5.0.4), IsBookmarked (4.6.11), and vector/embeddings search (5.0.4) for semantic retrieval alongside keyword search.

Views and Twig. Rendering is view-configuration driven, not controller-per-page. Configure ibexa.content_view rules, then in Twig use ibexa_render(content, { 'view': 'line' }) — the function now accepts parameters (5.0.7). Custom controllers should return ContentView objects so fragment caching and view matchers stay intact.

Siteaccess. Multi-site routing is matcher-driven (HostText, UriText, CompoundMatcher, custom SiteAccessMatcher services). If you store matcher configuration as JSON/YAML, use the SerializerFactory for matchers (5.0.5) rather than hand-rolled normalizers, and keep the CompoundMatcherNormalizer autoconfiguration in serializers.yml.

Events and decoration over patches. React to writes via the repository event classes (Ibexa\Contracts\Core\Repository\Events\...) and cross-cutting concerns via service decoration (ContentService, SearchService, handlers). Avoid touching src/lib internals; decorate at the contract boundary.

IO and binaries. Use IOService with binary create/update structs rather than raw filesystem calls; run php bin/console ibexa:io:migrate-files in batches when moving storages (batching fix in 5.0.5, reference-aware external storage in 5.0.7). Image hashing is deterministic as of 5.0.7, so re-imports stop duplicating files.

Notifications are a first-class API in 4.6.22+ (NotificationService, filtering, mark-as-unread, bulk mark-as-read). Implement TypedNotificationRenderer so notification types get labelled in the UI instead of raw internal names.

Content types as code. Keep content type definitions in YAML migrations rather than clicking them into a database; the admin UI is not a source of truth you can review.

Gotchas and Tips

  • Query::$limit defaults to 10. The most common "missing results" bug. Set it and paginate via $result->totalCount.
  • Search engine parity is not guaranteed. FullText, facets/aggregations, score sorting and embeddings need Solr/ES; the legacy SQL engine silently degrades or rejects criteria. Integration-test against the engine you actually deploy.
  • Pin a version with the known search fixes. Sorting in content filtering (5.0.5 / 4.6.27), StringField index length limits (5.0.9), and multilingual empty-option handling for Selection (5.0.8) are all recent. Old minors will bite.
  • Long strings in StringField get truncated in the index, not in storage — you get a "wrong results only" symptom that is painful to trace. Use the appropriate field type for long content.
  • HTTP 500 → 403 change. Image transformation ForbiddenException is now AccessDeniedHttpException (5.0.4); update tests and error handling. The Content-Length header is also now strictly typed.
  • Authorization: use the permission resolver, not the request. _access_control_attributes is populated on the request (5.0.7) which is handy for logging, but never gate business logic on it.
  • Custom user trees: if you build groups outside the default root, set users_group_root_subtree_path (4.6.22) or subtree-based role limitations will match nothing.
  • Hyphenated hosts/URIs in siteaccess matchers fail before 4.6.22 — a classic "works locally, 404 in staging on my-site.example" trap.
  • Decorated fragment renderers and ibexa_render disagreed before 4.6.22 (IBX-10116). If you decorate rendering, upgrade or your custom renderer is bypassed.
  • License is NOASSERTION in the metadata — open vendor/ibexa/core/LICENSE and confirm before shipping. Commercial features (cloud, AI embeddings backends) may depend on separately licensed bundles, and 5.x checks for ibexa/cloud.
  • Don't co-install with a Laravel app. Symfony 7.4 + PHP 8.3/8.4 will collide with Laravel's pinned Symfony components. Ibexa lives in its own application; integrate over HTTP/GraphQL/REST, not by sharing a container.
  • Debugging toolkit: bin/console debug:container 'Ibexa\Contracts\Core\Repository\SearchService', the Symfony profiler's Ibexa data collector (priority-configurable since 4.6.19), bin/console ibexa:reindex after index-affecting changes, and cache:clear plus HTTP-cache purge after writes — reverse-proxy invalidation is not part of the PAPI transaction.
  • Low stars / zero dependents means nothing here. ibexa/core is a monorepo read-only split consumed via ibexa/oss and friends; judge it by the release cadence (5.0.x and 4.6.x maintained in parallel) and the issue tracker, not GitHub popularity. Track both branches: 4.6 still receives fixes you may need to backport mentally.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource