PHPackages                             kaliop/ezobjectwrapperbundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. kaliop/ezobjectwrapperbundle

ActiveSymfony-bundle[Utility &amp; Helpers](/categories/utility)

kaliop/ezobjectwrapperbundle
============================

Easy wrappers for your eZPlatform/eZPublish5 content/location objects

v4.0.0(8y ago)910.1k5[6 issues](https://github.com/kaliop/ezobjectwrapper/issues)GPL-2.0PHPPHP &gt;=5.4CI failing

Since Feb 10Pushed 5y ago7 watchersCompare

[ Source](https://github.com/kaliop/ezobjectwrapper)[ Packagist](https://packagist.org/packages/kaliop/ezobjectwrapperbundle)[ RSS](/packages/kaliop-ezobjectwrapperbundle/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (2)Dependencies (5)Versions (28)Used By (0)

eZObjectWrapperBundle
=====================

[](#ezobjectwrapperbundle)

A Symfony Bundle for eZPlatform / eZPublish 5 development (&gt;=5.3).

Developed by the [Kaliop](http://www.kaliop.com/) team.

Description
-----------

[](#description)

This bundle offers a simple model to organize all the "business logic" code which deals with eZPublish Location and Content objects, keeping it away from Controllers, Commands and other application layers which relate to the Framework.

This way, if you change the definition of one of your ContentTypes, you will not have to hunt across the whole codebase to apply fixes but, ideally, change the code only in one place.

It also tries to adopt patterns which are familiar to Symfony developers who have not used eZPublish before. NB: if you are a Symfony developer please read the *This is not your grandpa's ORM* chapter further down to help avoid common pitfalls.

How it works
------------

[](#how-it-works)

The bundle provides an *Entity Manager* service, which is used to retrieve Repository services. For each specific type of Content, a different Repository service is used.

*Repository* services are used to hold the logic to fetch Entities. A typical Repository method would be f.e. `getLastTenModified()`.

*Entity* instances provide a lazy-loading wrapper for Contents and Locations. They are supposed to hold the logic to decode data from the Content Fields, and to fetch related Entities (without overloading the view Controller and creating a new kernel request). They are generally *not* configured as Symfony services (for a starter, they are not singletons).

Developers are supposed to create a new Repository and Entity class for each of the Content Types in use in the website; the easiest way to do so is to subclass the existing ones and just add in the custom business logic.

The twig helper
---------------

[](#the-twig-helper)

This bundle also provides a Twig function, `renderLocation`, which uses the ViewController as a service, and doesn't relaunch the Symfony kernel, for faster execution.

Installation
------------

[](#installation)

The recommended way to install this bundle is through [Composer](http://getcomposer.org/).

- Add the `kaliop/ezobjectwrapperbundle` package into your composer.json file :

```
{
    "require": {
        "kaliop/ezobjectwrapperbundle": "~3.0"
    }
}
```

- Add eZObjectWrapperBundle to EzPublishKernel.php:

```
new \Kaliop\eZObjectWrapperBundle\KaliopeZObjectWrapperBundle(),
```

Usage
-----

[](#usage)

### Retrieving Entities

[](#retrieving-entities)

```
$entityManager = $this->container->get('ezobject_wrapper.entity_manager');
$repository = $entityManager->getRepository('article');
$articleEntity = $repository->loadEntityFromLocationId(2);
echo 'Article entity 2 is named: ' . $articleEntity->content()->contentInfo->name;
echo 'Article entity 2 has parent Id: ' . $articleEntity->location()->parentLocationId;
```

There are many more methods available in the Repository, you can retrieve an Entity by id, Content, Location, LocationId and even Remote Ids.

### Calling methods or attributes of an Entity in Twig

[](#calling-methods-or-attributes-of-an-entity-in-twig)

```
{{ ez_field_value(articleEntity.content, 'title') }}
```

### Twig function render\_location

[](#twig-function-render_location)

This does render a Location without using a separate Controller and reloading the kernel

```
{{ render_location(locationId, 'view_type', {}) }}
```

### Creating your own Entities and Repositories

[](#creating-your-own-entities-and-repositories)

Let's imagine you want to handle the 'newsletter' content type.

1. Create an Entity class:

    ```
    namespace Acme\AcmeBundle\Entity;
    use Kaliop\eZObjectWrapperBundle\Core\Entity as BaseEntity;

    class Newsletter extends BaseEntity
    {
    }
    ```
2. Create a Repository class, which relates to that:

    ```
    namespace Acme\AcmeBundle\Repository;
    use Kaliop\eZObjectWrapperBundle\Core\Repository as BaseRepository;

    class Newsletter extends BaseRepository
    {
        protected $entityClass = '\Acme\AcmeBundle\Entity\Newsletter';
    }
    ```
3. Register the Repository with the Entity Manager

    ```
    ezobject_wrapper:
        class_map:
            newsletter: \Acme\AcmeBundle\Repository\Newsletter
    ```

    (Note that this is "plain" configuration, it does not have to be in `parameters`)
4. Test that it works

    ```
    $entityManager = $this->container->get('ezobject_wrapper.entity_manager');
    $repository = $entityManager->getRepository('newsletter');
    ```
5. Add new methods to the Entity and Repository classes

    ```
    namespace Acme\AcmeBundle\Repository;
    use ...;

    class Newsletter extends BaseRepository
    {
        protected $entityClass = '\Acme\AcmeBundle\Entity\Newsletter';

        /**
         * @return \Acme\AcmeBundle\Entity\Newsletter[]
         */
        public function getAllNewsletters()
        {
            $query = new Query();
            $query->filter = new Criterion\LogicalAnd(array(
                new Criterion\ContentTypeIdentifier($this->contentTypeIdentifier),
                new Criterion\Subtree('/1/2/212') // root node where all newsletters are located
            ));
            $query->performCount = false;
            $query->limit = PHP_INT_MAX-1;
            $query->offset = 0;
            // A helper method made available from the base repository class
            return $this->loaddEntitiesFromSearchResults(
                $this->getSearchService()->findContent($query)
            );
        }
    }

    namespace Acme\AcmeBundle\Entity;
    use ...;

    class Newsletter extends BaseEntity
    {
        protected $issueTypeIdentifier = 'newsletter_issue';

        /**
         * @return \DateTime
         */
        public function getLatestIssueDate()
        {
            $query = new Query();
            $query->filter = new Criterion\LogicalAnd(array(
                new Criterion\ContentTypeIdentifier($this->issueTypeIdentifier),
                new Criterion\Subtree($this->location()->pathString)
            ));
            $query->performCount = false;
            $query->limit = 1;
            $query->offset = 0;
            $query->sortClauses = array(new DatePublished(Query::SORT_DESC));
            $result = $this->repository->getSearchService()->findContent($query);
            if (count($result->searchHits) > 0) {
                $latest = $result->searchHits[0];
                return $latest->valueObject->contentInfo->publishedDate;
            }
            return new \DateTime("@0");
        }
    }
    ```

Advanced Usage
--------------

[](#advanced-usage)

### Registering new Repositories as services

[](#registering-new-repositories-as-services)

We have seen above how to register a php class as Repository. Another way to register classes as repositories is to use tagged Symfony services. The main advantage that you get in exchange for a little bit more configuration is that you can now inject configuration settings into the repository.

Example:

```
services:
    ezobject_wrapper.repository.newsletter:
        class: \Acme\AcmeBundle\Repository\Newsletter
        parent: ezobject_wrapper.repository.abstract
        arguments:
            - @ezpublish.api.repository
            - @ezobject_wrapper.entity_manager
        calls:
            # Injecting some settings to our custom repository class. E.g. the root path of newsletter contents
            - [ setSettings, [ { newsletter_location_path: %newsletter_location_path% } ] ]
        tags:
            # Tagging the service will make it register with the Entity Manager for the given contentType
            -  { name: ezobject_wrapper.repository, content_type: newsletter }

parameters:
    # Using a parameter allows to easily set different values for different environments for things like location Ids
    newsletter_location_path: /1/2/212/
```

Then, inside the Acme\\AcmeBundle\\Repository class, you can use the *settings* member:

```
    ...
    $query->filter = new Criterion\LogicalAnd(array(
        new Criterion\ContentTypeIdentifier($this->contentTypeIdentifier),
        new Criterion\Subtree($this->settings['newsletter_location_path']) // root node where all newsletters are located
    ));
    ...
```

NB: if you want to make sure that the settings injected into your custom Repository are always valid, you simply have to implement the `validateSettings` method

### Passing configuration settings into the Entities

[](#passing-configuration-settings-into-the-entities)

Since Entity classes are not registered as Symfony services, injecting settings into Entity instances might seem problematic at first instance. The *enrichEntityAtLoad* method is available in Repository classes for that purpose.

```
namespace Acme\AcmeBundle\Repository;
use ...;

class Newsletter extends BaseRepository
{
    protected $entityClass = '\Acme\AcmeBundle\Entity\Newsletter';

    /**
     * @return \Acme\AcmeBundle\Entity\Newsletter[]
     */
    protected function enrichEntityAtLoad($entity)
    {
        $entity = parent::enrichEntityAtLoad($entity);
        return $entity->setIssueTypeIdentifier('newsletter_issue');
    }
}

namespace Acme\AcmeBundle\Entity;
use ...;

class Newsletter extends BaseEntity
{
    protected $issueTypeIdentifier;

    /**
     * @return $this
     */
    public function setIssueTypeIdentifier($issueTypeIdentifier)
    {
        $this->issueTypeIdentifier = $issueTypeIdentifier;
        return $this;
    }
}
```

### Allowing an Entity to generate URLs to its Location view

[](#allowing-an-entity-to-generate-urls-to-its-location-view)

For this common scenario, Traits are made available, to be added to bot Repository an Entity classes

```
namespace Acme\AcmeBundle\Repository;
use ...;

class Newsletter extends BaseRepository
{
    use Kaliop\eZObjectWrapperBundle\Core\Traits\RouterInjectingRepository;
}

namespace Acme\AcmeBundle\Entity;
use ...;

class Newsletter extends BaseEntity
{
    use Kaliop\eZObjectWrapperBundle\Core\Traits\UrlGeneratingEntity;

    /**
     * To be used when absolute urls to this Location have to be generated, and there is no twig template or routing service available
     * @return string
     */
    public function absoluteUrl()
    {
        return $this->getUrlAlias(true);
    }
}
```

```
services:
    ezobject_wrapper.repository.newsletter:
        class: \Acme\AcmeBundle\Repository\Newsletter
        parent: ezobject_wrapper.repository.abstract
        arguments:
            - @ezpublish.api.repository
            - @ezobject_wrapper.entity_manager
        calls:
            - [ setRouter, [ '@router' ] ]
        tags:
            -  { name: ezobject_wrapper.repository, content_type: newsletter }
```

### Allowing an Entity to render RichText fields as html

[](#allowing-an-entity-to-render-richtext-fields-as-html)

RichText fields need the help of a Symfony service to convert their xml content to html. Again, Traits are made available for that, to be added to bot Repository an Entity classes.

```
namespace Acme\AcmeBundle\Repository;
use ...;

class Newsletter extends BaseRepository
{
    use \Kaliop\eZObjectWrapperBundle\Core\Traits\RichTextConverterInjectingRepository;
}

namespace Acme\AcmeBundle\Entity;
use ...;

class Newsletter extends BaseEntity
{
    use \Kaliop\eZObjectWrapperBundle\Core\Traits\RichTextConvertingEntity;

    /**
     * @return string
     */
    public function bodyAsHtml()
    {
        return $this->getHtml($this->content()->getField('body')->xml);
    }
}
```

```
services:
    ezobject_wrapper.repository.newsletter:
        class: \Acme\AcmeBundle\Repository\Newsletter
        parent: ezobject_wrapper.repository.abstract
        arguments:
            - @ezpublish.api.repository
            - @ezobject_wrapper.entity_manager
        calls:
            - [ setRichTextConverter, [ '@ezpublish.fieldType.ezxmltext.converter.html5' ] ]
        tags:
            -  { name: ezobject_wrapper.repository, content_type: newsletter }
```

### Allowing an Entity to fetch related Entities

[](#allowing-an-entity-to-fetch-related-entities)

A common usecase is when, given an instance of an Entity, you want to fetch its related object(s) as Entities too. A Trait is available for this case as well:

```
Kaliop\eZObjectWrapperBundle\Core\Traits\RelationTraversingEntity

```

Just add it to your Entity class and you will be able to use 2 new methods to retrieve the contents of its object relation(s) fields:

```
$relatedEntity = $this->getRelation('fieldName');
$relatedEntitiesArray = $this->getRelations('anotherFieldName');

```

Impact with the caches (a.k.a. don't shoot yourself in the foot)
----------------------------------------------------------------

[](#impact-with-the-caches-aka-dont-shoot-yourself-in-the-foot)

When using the render\_location twig helper, or using subclasses of Entity which fetch other Content objects in helper methods, be aware that you are introducing caching dependencies.

eZPublish by default goes to great lengths to make sure that the caches which keep the 'html version' of Content objects are automatically expired whenever the objects are edited. One might say that view-cache-expiration is the prime reason for using Symfony sub-requests when displaying a list of Content objects.

Whenever you end up displaying a Location which is not the current one, remember to add its Id to the X-Location-Id header in your main controller response, so that eZPublish will know when to clear its cache.

For more details see:

This is not your grandpa's ORM
------------------------------

[](#this-is-not-your-grandpas-orm)

The EntityManager, Repository and Entity classes in eZObjectWrapper have only a vague resemblance with their counterparts in the Doctrine ORM. Please do not assume that you can use them the same way.

F.e., at the moment:

- Entities can not be persisted at all, except by using the eZPublish repository API
- There is no concept of creating Entities without attaching them to the EntityManager
- There is no DQL or other equivalent language
- the only Query builder available is the one from the eZPublish repository API

Contact
-------

[](#contact)

E-mail :  /

[![License](https://camo.githubusercontent.com/672917311e739c98173f16e30c449342a8da74ac2565f13bd487ee029b19c217/68747470733a2f2f706f7365722e707567782e6f72672f6b616c696f702f657a6f626a6563747772617070657262756e646c652f6c6963656e7365)](https://packagist.org/packages/kaliop/ezobjectwrapperbundle)[![Latest Stable Version](https://camo.githubusercontent.com/5a5d00c208cb5ab1fa9b2c23d53ac6e3d99eb334867eec4545d2facacfbc5b28/68747470733a2f2f706f7365722e707567782e6f72672f6b616c696f702f657a6f626a6563747772617070657262756e646c652f762f737461626c65)](https://packagist.org/packages/kaliop/ezobjectwrapperbundle)[![Total Downloads](https://camo.githubusercontent.com/013e5a128e34354df235d97a53dbc8375a08d4ed92833aef0a6abd16ef90edd8/68747470733a2f2f706f7365722e707567782e6f72672f6b616c696f702f657a6f626a6563747772617070657262756e646c652f646f776e6c6f616473)](https://packagist.org/packages/kaliop/ezobjectwrapperbundle)

[![Build Status](https://camo.githubusercontent.com/86f95b0bd068219d76877dcda6298541743dcd6811638fd9b9d1af8956508e64/68747470733a2f2f7472617669732d63692e6f72672f6b616c696f702f657a6f626a656374777261707065722e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/kaliop/ezobjectwrapper)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/cb8ddac50afbc6aeb3bf056b5c97dc9814f80f5c20396a92e92151b498e3331c/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f6b616c696f702f657a6f626a656374777261707065722f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/kaliop/ezobjectwrapper/?branch=master)[![Code Coverage](https://camo.githubusercontent.com/5cb317da0c722c25feafb0e068150cb103d30ed0e63783f6ca693c6345a5686b/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f6b616c696f702f657a6f626a656374777261707065722f6261646765732f636f7665726167652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/kaliop/ezobjectwrapper/?branch=master)

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance7

Infrequent updates — may be unmaintained

Popularity28

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity68

Established project with proven stability

 Bus Factor1

Top contributor holds 50% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~41 days

Recently: every ~128 days

Total

25

Last Release

3120d ago

Major Versions

v1.0.9 → v2.0.02015-09-04

v2.0.0 → v3.0.0-RC12015-11-11

2.0.1 → v3.0.02015-11-27

2.0.3 → v3.0.12017-01-09

v3.1.0 → v4.0.02017-11-01

PHP version history (2 changes)1.0PHP &gt;=5.3.3

v3.0.0-RC1PHP &gt;=5.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/f641f7d6edabf83c56f6c4998b04e6c59a00b46bf51ca6699c31b09f9c948677?d=identicon)[gggeek](/maintainers/gggeek)

---

Top Contributors

[![Erilan](https://avatars.githubusercontent.com/u/2326428?v=4)](https://github.com/Erilan "Erilan (6 commits)")[![sbressey](https://avatars.githubusercontent.com/u/44765351?v=4)](https://github.com/sbressey "sbressey (3 commits)")[![gggeek](https://avatars.githubusercontent.com/u/308634?v=4)](https://github.com/gggeek "gggeek (2 commits)")[![bdunogier](https://avatars.githubusercontent.com/u/235928?v=4)](https://github.com/bdunogier "bdunogier (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/kaliop-ezobjectwrapperbundle/health.svg)

```
[![Health](https://phpackages.com/badges/kaliop-ezobjectwrapperbundle/health.svg)](https://phpackages.com/packages/kaliop-ezobjectwrapperbundle)
```

###  Alternatives

[ezsystems/legacy-bridge

eZ Platform bridge to eZ Publish Legacy

17118.6k4](/packages/ezsystems-legacy-bridge)[netgen/remote-media-bundle

Remote media field type implementation

189.4k4](/packages/netgen-remote-media-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
