Revision: Wed, 19 Jan 2022 11:39:05 GMT

Custom behaviors (Extension)

Custom behaviors are the primary way of adding functionality for entities. Behaviors might be anything from a great way to work with dates like Carbon or a behavior that allows you to create slug from Post title property. You have the ability to create your own behaviors and share them with the community.

Custom behaviors are classes that allow implementing custom logic for your entities. They extend Cycle\ORM\Entity\Behavior\Schema\BaseModifier and implement the custom logic in abstract methods.


In the following example we will show you how it is simple to create a custom behavior:


At first, you have to describe the attribute (annotation) that will be used in an entity.

use Cycle\ORM\Entity\Behavior\Schema\BaseModifier;
use Cycle\ORM\Entity\Behavior\Schema\RegistryModifier;
use Cycle\Schema\Registry;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
use Doctrine\Common\Annotations\Annotation\Target;

 * @Annotation
 * @NamedArgumentConstructor()
 * @Target({"CLASS"})
#[\Attribute(\Attribute::TARGET_CLASS), NamedArgumentConstructor]
final class Sluggable extends BaseModifier
    public function __construct(
        // Properties should present in your entity with `[#Column(type: 'string')]` attribute
        private string $field = 'slug',     
        private string $from = 'title'
    ) {

    protected function getListenerClass(): string
        return SluggableListener::class;

    protected function getListenerArgs(): array
        // Will be passed to the listener constructor
        return [
            'field' => $this->field,
            'from' => $this->from

Attribute listener

Then you have to describe listener connected with Sluggable attribute via Sluggable::getListenerClass method.

use Cycle\ORM\Entity\Behavior\Attribute\Listen;
use Cycle\ORM\Entity\Behavior\Event\Mapper\Command\OnCreate;
use Cycle\ORM\Entity\Behavior\Event\Mapper\Command\OnUpdate;
use Cocur\Slugify\Slugify;

final class SluggableListener
    public function construct(
        private Slugify $slugify,
        private ORMInterface $orm,
        private string $field
        private string $from
    ) {

    public function invoke(OnCreate|OnUpdate $event): void
        $slug = $this->slugify->slugify($event->state->getData()[$this->from]);
        $isExist = $this->orm->getRepository($event->role)->findOne([$this->field => $slug]) !== null;

        if ($isExist) {
            $slug .= '-' . time();

        $event->state->register($this->field, $slug);

Available events:

Example of usage

use Cycle\Annotated\Annotation\Column;
use Cycle\Annotated\Annotation\Entity;

    field: 'slug', 
    from: 'title'
class Comment
    #[Column(type: 'primary')]
    private int $id;

    #[Column(type: 'string')]
    private string $title;

    #[Column(type: 'string')]
    private string $slug = '';
Edit this page