Modules

The Versioning module provides a Repository API.

Architecture

  • Versioning\RepositoryManager holds various repositories
  • Versioning\RepositoryService holds a repository of type Versioning\Entity\RepositoryInterface and multiple revisions of type Versioning\Entity\RevisionInterface
  • Versioning\Entity\RepositoryInterface must be implemented by Models/Entities that will be used as a repository
  • Versioning\Entity\RevisionInterface must be implemented by Models/Entities that will be used as a Revision

Changes are not persistent! That means, an ObjectManager needs to take care of it!

Setup

Repository.php

class Repository implements \Versioning\Entity\RepositoryInterface
{
    // ...
}

Revision.php

class Revision implements \Versioning\Entity\RevisionInterface
{
    // ...
}

Usage

Declare a repository

$repository = new Repository();
$serviceManager->get('Versioning\RepositoryManager')->addRepository($repository);

// Store changes in the database
$entityManager->persist($repository);
$entityManager->flush();

Fetch the RepositoryService

$repositoryService = $serviceManager->get('Versioning\RepositoryManager')->getRepository($repository);

Add a revision

$revision = new Revision();
$revision->setId(2);
$revision->setContent('Testinhalt');
$repositoryService->addRevision($revision);

// Store changes in the database
$entityManager->persist($revision);
$entityManager->flush();

Find a revision

$repositoryService->getRevision(2);

Check out a revision

$repositoryService->checkoutRevision(2);

// Store changes in the database
$entityManager->persist($repositoryService->getRepository());
$entityManager->flush();

Get the current revision

$revision = $repositoryService->getCurrentRevision();

Get the latest revision

$revision = $repositoryService->getHead();

Remove a revision from the repository

$revision = $repositoryService->getRevision(2);
$repositoryService->removeRevision(2);

// Store changes in the database
$entityManager->remove(revision);
$entityManager->flush();

The Uuid module provides "universal unique ids" for any object.

Architecture

  • UUid\Manager\UUidManager implements UUid\Manager\UUidManagerInterface: The Manager used for finding and creating uuids.
  • UUid\Entity\Uuid implements Uuid\Entity\UuidInterface: The Entity stored in the uuid table.
  • UUid\Entity\UuidEntity implements Uuid\Entity\UuidHolder: An Entitiy that has a uuid.

// Der ServiceManager kennt UUid\Manager\UUidManager.
The ServiceManager knows about UUid\Manager\UUidManager.

// #### Achtung

Warning

$uuidManager->create() flushes the `ObjectManager due to restrictions in Doctrine!

Setup

Consider blog entries that have Uuids.

BlogPost.php

class BlogPost extends UuidEntity {
    // ...
}

Create a new BlogPost

First way

$blogPost = new BlogPost();
$uuidManager->injectUuid($blogPost);
$uuidManager->getObjectManager()->flush();

Second way

$blogPost = new BlogPost();
$uuid = $uuidManager->create();
$uuidManager->injectUuid($blogPost, $uuid);
$uuidManager->getObjectManager()->flush();

Third way

class BlogPost extends UuidEntity{
    public function __construct(){
        $this->setUuid(new Uuid());
    }
}

$blogPost = new BlogPost();

$this->getObjectManager()->persist($blogPost->getUuid());
$this->getObjectManager()->persist($blogPost);
$this->getObjectManager()->flush();

Access the uuid

echo $blogPost->getId();

Access the uuid hash

echo $blogPost->getUuid();

The User module takes care of everything related to users.

Architecture

  • UserManager holds the Users
  • UserService hold the entity
  • User the entity

Setup

No setup required.

Get the current session's user

$user = $userManager->getUserFromRequest();
if(!$user){
    echo "not logged in";
} else {
    echo "hello ".$user->getUsername();
}

Create a User

$user = $userManager->createUser(array(
    'usename'  => 'Richard Stallman',
    'password' => 'foaijwf293foa0wf23',
    //..
));
$userManager->getObjectManager()->flush();

Find a User

// by user id
$user = $userManager->getUser(1);

// by username
$user = $userManager->findUserByUsername('foobar');

// by email
$user = $userManager->findUserByUsername('foo@bar.de');

By user Id

$user = $userManager->getUser(1);

By username

$user = $userManager->findUserByUsername('foobar');

By email

$user = $userManager->findUserByUsername('foo@bar.de');

The Term module manages words which may be used by the `Taxonomy˚.

Architecture

  • TermManager manages Terms
  • TermService holds a Term
  • Term the entity

Setup

No setup required.

Find a Term

// by id
$term = $termManager->getTerm(1);

// by name
$term = $termManager->findTermByName('foobar');

// by slug
$term = $termManager->findTermBySlug('foobar');

// Das Taxonomymodul ist zuständig, um Taxonomien (zb. Kategorie, Land, Lehrplan, ...) zu verwalten.
// Die Datenstruktur ist ein Baum wobei es nur eine Wurzel geben sollte (um die Taxonomien zentral verwalten zu können), jeder Knotenpunkt kann einen konfigurierbaren Typen haben.
The Taxonomy module manages taxonomies, like "category", "country", "curriculum"...
The underlying datastructure is a tree. Each node can be of a configurable type.

Architecture

// * SharedTaxonomyManager implements SharedTaxonomyManagerInterface verwaltet die unterschiedlichen Taxonomy Typen (zb. Kategorie, Lehrplan), kann aber auch Terme über die ID finden
// * TaxonomyManager implements TaxonomyManagerInterface stellt einen Taxonomy Typen da, welcher mehrere Terme hält.
// * TaxonomyService implements TaxonomyServiceInterface ist ein einzelner Term, wobei es sich um einen Delegator bzw eine Fassade hält.
// * TaxonomyType implements TaxonomyTypeInterface ist ein Taxonomy Typ (zb "category" oder "curriculum")
// * Taxonomy implements TaxonomyInterface ist ein Taxonomy Typ in einer bestimmten Sprache (zb "category" & "de")
// * TaxonomyTerm implements TaxonomyTermInterface ist der eigentliche Term, wobei die Inhalte des Terms speratat über das Term Modul gespeichert werden, um doppelte Inhalte zu vermeiden.

  • SharedTaxonomyManager implements SharedTaxonomyManagerInterface manages various Taxonomy types (e.g. "category" oder "curriculum"), but can also find Terms by their id.
  • TaxonomyManager implements TaxonomyManagerInterface represents a Taxonomy type, which manages multiple Terms.
  • TaxonomyService implements TaxonomyServiceInterface a delegator/Ffssade managing a single Term
  • TaxonomyType implements TaxonomyTypeInterface is a Taxonomy type (e.g. "category" oder "curriculum")
  • Taxonomy implements TaxonomyInterface is a Taxonomy type in a given language (e.g. "category" & "en")
  • TaxonomyTerm implements TaxonomyTermInterface is the actual Term. The Term's contents are stored seperately via the Term module in order to avoid redundancy.

Configuration

module.config.php

return array(
    'taxonomy' => array(

        // define your associations here
        'associations' => array(

            // you can get the associated entities with $termService->getAssociated('foolinks');
            'foolinks' => function  (ServiceLocatorInterface $sm, $collection)
            {
                return $sm->get('FoobarService')->doSomething($collection);
            }

        ),

        // define your types here
        'types' => array(

            // type 'foo'
            'foo' => array(
                'options' => array(

                    // no associations are allowed. You can not use $termService->getAssociated('foolinks')!
                    'allowed_associations' => array(
                    ),

                    // the only parent type allowed is 'root'. Note that you can't add foo to foo, as it is not an allowed parent type
                    'allowed_parents' => array(
                        'root',
                    ),

                    // use custom templates
                    'templates' => array(

                        // use a custom template for the update view
                        'update' => 'taxonomy/taxonomy/update'
                    ),

                    // radix disabled, you can't set parent_id to null!
                    'radix_enabled' => false
                )
            ),
            'bar' => array(
                'options' => array(

                    // Associations are allowed. You can use $termService->getAssociated('foolinks')!
                    'allowed_associations' => array(
                        'foolinks'
                    ),

                    // self-referencing is allowed here
                    'allowed_parents' => array(
                        'foo',
                        'bar'
                    ),
                    'radix_enabled' => false
                )
            ),

            // this is the root, it should always be present
            'root' => array(
                'options' => array(
                    'radix_enabled' => true
                )
            ),
        )
    ),
);

Usage

Find a Taxonomy

// by id
$sharedTaxonomyManager->getTaxonomy(1);

// by name (type) and language
$sharedTaxonomyManager->findTaxonomyByName('foo', $languageService);

Find term via the SharedTaxonomyManager

$term = $sharedTaxonomyManager->getTerm(3);
echo $term->getId(); // outputs '3'

Find a Term via a TaxonomyManager

// by id
$term = $taxonomyManager->getTerm(3);
echo $term->getId(); // outputs '3'

// by parent
$ancestors = explode('/', 'path/to/term');
$term = $taxonomyManager->findTermByAncestors($ancestors);
echo $term->getName(); // outputs 'term'

Find the root(s) of a type.

Find all Terms of a type that do not have a parent or are of a different type then their parents.

$result = $taxonomyManager->getSaplings();
foreach($result as $term){
    echo $term->getId();
}

Find related Terms

All children

$children = $termService->getChildren();
foreach($children as $term){
    echo $term->getId();
}

All children of a specific type

$children = $termService->findChildrenByTaxonomyName('bar');
foreach($children as $term){
    echo $term->getId();
}

Find parent

$parent = $termService->getParent();

Associations

// Damit Assoziationen funktionieren, muss zunächst eine m:n Tabelle von der zu Assoziierenden Tabelle zu term_taxonomy in der Datenbank angelegt werden. Außerdem muss Doctrine\ORM wissen, dass es diese Assoziation gibt, womit dem Entity ein neues Attribut verliehen werden muss.
Before a Association can be used, a m:n table must be created between term_taxonomy and the table to be associated.
Additionally Doctrine\ORM needs to know about the association.

Creating an Association

$termService = $sharedTaxonomyManager()->getTaxonomy('', $languageService)->getTerm(1)
$foolinks = $termService->associate('foolinks', $foolinkEntity);
echo $termService->isAssociated('foolinks', $foolinkEntity); // outputs: true

// make things persistent
$sharedTaxonomyManager()->getObjectManager()->flush();

Fetching assiciated elements

$termService = $sharedTaxonomyManager()->getTaxonomy('', $languageService)->getTerm(1)
$foolinks = $termService->getAssociated('foolinks');

Removing an association

$termService = $sharedTaxonomyManager()->getTaxonomy('', $languageService)->getTerm(1)
$foolinks = $termService->removeAssociation('foolinks', $foolinkEntity);
echo $termService->isAssociated('foolinks', $foolinkEntity); // outputs: false

// make things persistent
$sharedTaxonomyManager()->getObjectManager()->flush();

The ClassResolver module finds concrete implementation of interfaces.

Configuration

module.config.php

return array(
    'class_resolver' => array(
        'FooInterface' => 'Foo',
        'BarInterface' => 'Bar',
    ),
);

Resolve an interface

$classResolver = $serviceManager->get('ClassResolver\ClassResolver');

echo $classResolver->resolveClassName('FooInterface'); // 'Foo'
$classResolver = $serviceManager->get('ClassResolver\ClassResolver');

$bar = $classResolver->resolve('BarInterface'); // equivalent to `$bar = $serviceLocator->get('Bar');`
  • Work in Progress *

Events:
* createEntity.preFlush (Entity\Service\EntityServiceInterface $entity, array $params)
* createEntity.postFlush (Entity\Service\EntityServiceInterface $entity, array $params)

  • deleteEntity.preFlush (Entity\Service\EntityServiceInterface $entity, array $params)
  • deleteEntity.postFlush (Entity\Service\EntityServiceInterface $entity, array $params)
  • Work in Progress *

The Tokenizer module manages the replacement of tokens in strings. For exmaple, hello {user} becomes hello foobar.

Usage

Set up the provider

To enable the replacement of tokens, you have to implement a provider for the data:

Provider.php

use Token\Provider\ProviderInterface;
use Token\Provider\AbstractProvider;

class TokenizerProvider extends AbstractProvider implements ProviderInterface
{

    public function getData()
    {
        /**
         *
         * Tokens
         */
        return array(
            'title' => $this->getObject()->getTitle(),
            'category' => $this->getObject()->getCategory()->getName(),
            'id' => $this->getObject()->getId()
        );
    }

    protected function validObject($object)
    {
        // Optimalerweise steht hier zum Beispiel: if( ! $object instanceOf FooInterface )
        if (! is_object($object) )
            throw new \InvalidArgumentException(sprintf('Expected PostInterface but got `%s`', get_class($object)));
    }
}

Use the tokenizer

SomeService.php

class SomeService {
    use \Token\TokenizerAwareTrait;

    public function foobar($object)
    {
        $string = $this->getTokenizer()->transliterate('TokenizerProvider', $object, 'token/{id}-{title}');
        echo $string; // output: token/1-test
    }
}

The Alias module beautifies the URLs. For example, /page/view/1 becomes /alias/page/association/finances.

Features

  • Decoupled: The AliasManager is decoupled from the rest of the software and can therefore be used more flexibly.
  • Fallbacks: Fallbacks ensure that no problems arise during the allocation of aliases.
  • AutoAlias: You can configure automatic aliasing.
  • Listeners: Listeners make the creation of aliases easy by using automatic aliasing.
  • UrlGenerating: The URL ViewHelper checks whether an alias already exists for the given URL and returns it.
  • Aliases do not become obsolete: It is possible to define more than one alias for a given URL. Thus changing titles do not affect the availability of the content.

Create aliases

Thanks to Listeners, the creation of aliases is easy:

module.config.php

return array(
    'alias_manager' => array(
        'aliases' => array(
            'page' => array(

                // Siehe token guide
                'tokenize' => 'page/{category}/{title}',
                'provider' => 'TokenizerProvider',
                'fallback' => 'page/{category}/{id}-{title}'
            )
        )
    )
);

Listener.php

use Alias\Listener\AbstractListener;
use Zend\EventManager\Event;

class BlogControllerListener extends AbstractListener
{

    /**
     * Gets executed on 'onUpdate'
     *
     * @param Event $e
     * @return null
     */
    public function onUpdate(Event $e)
    {
        $page = $e->getParam('page');
        $entity = $page->getEntity();
        $language = $e->getParam('language');

        $url = $e->getTarget()
            ->url()
            ->fromRoute('page/view', array(
            'page' => $page->getId()
        ));

        $this->getAliasManager()->autoAlias('page', $url, $entity, $language);
    }

    public function attachShared(\Zend\EventManager\SharedEventManagerInterface $events)
    {
        $this->listeners[] = $events->attach('SomeController', 'update', array(
            $this,
            'onUpdate'
        ));
    }
}

SomeController.php

use Zend\Mvc\Controller\AbstractActionController;

class SomeController extends AbstractActionController
{
    use \Language\Manager\LanguageManagerAwareTrait, \Common\Traits\ObjectManagerAwareTrait;

    public function updateAction()
    {

        $data = $this->params()->fromPost();
        // Siehe user guide
        $language = $this->getLanguageManager()->getLanguageFromRequest();

        $id = $data['id'];
        $title = $data['title'];
        $content = $data['content'];
        $page = $this->updatePage($id, $title, $content);

        $this->getEventManager()->trigger('update', $this, array(
            'page' => $page,
            'language' => $language
        ));

        $this->getObjectManager()->flush();

        return 'saved!';
    }
}

Some content is published under a specific license.

Configuration

You can specify a default license for each language:

module.config.php

return [
    'license_manager' => [
        'defaults' =>
        [
            'de' => 'cc-by-sa-3.0',
            'en' => 'cc-by-sa-3.0'
        ]
    ],
]

In this example, the manager will search the database for a license with language en and title cc-by-sa-3.0.

Usage

Implement LicenseAwareInterface

To use the LicenseManager for injection of licenses, the Entity or Model has to implement the LicenseAwareInterface.

Entity.php

class Entity implements LicenseAwareInterface
{
    // ...
}

A concrete implementation (using Doctrine) might look as follows:

use Doctrine\ORM\Mapping as ORM;

// ORM definitions like ORM\Entity or ORM\Table
class Entity implements LicenseAwareInterface
{
    /**
     * @ORM\ManyToOne(targetEntity="License\Entity\LicenseInterface")
     */
    protected $license;

    public function getLicense()
    {
        return $this->license;
    }

    public function setLicense (LicenseInterface $license)
    {
        $this->license = $license;
        return $this;
    }
}

In this case, LicenseInterface is not a mistake but will be resolved by Doctrine automatically.

Inject licenses

If you pass no additional parameters, the default license will be injected. In this case, the LicenseManager fetches the current request language from the LanguageManager:

class SomeService
{
    use \License\Manager\LicenseManagerAwareTrait;

    public function createEntity()
    {
        $entity = new Entity();
        $this->getLicenseManager()->injectLicense($entity);
    }
}

A custom license has to be passed as an additional parameter:

class SomeService
{
    use \License\Manager\LicenseManagerAwareTrait;

    public function createEntity()
    {
        $entity = new Entity();
        $license = $this->getLicenseManager()->getLicense(3); // Finds a license with the id 3
        $this->getLicenseManager()->injectLicense($entity, $license);
    }
}