\Field\Model\BehaviorFieldableBehavior

Fieldable Behavior.

A more flexible EAV approach. Allows additional fields to be attached to Tables. Any Table (Contents, Users, etc.) can use this behavior to make itself fieldable and thus allow fields to be attached to it.

The Field API defines two primary data structures, FieldInstance and FieldValue:

  • FieldInstance: is a Field attached to a single Table. (Schema equivalent: column)
  • FieldValue: is the stored data for a particular [FieldInstance, Entity] tuple of your Table. (Schema equivalent: cell value)

This behavior allows you to add virtual columns to your table schema.


Events triggered by this behavior:

  • Field..Entity.beforeFind
  • Field..Entity.fieldAttached
  • Field..Entity.validate
  • Field..Entity.beforeSave
  • Field..Entity.afterSave
  • Field..Entity.beforeDelete
  • Field..Entity.afterDelete

Summary

Methods
Properties
Constants
__construct()
implementedEvents()
beforeFind()
attachEntityAttributes()
beforeSave()
afterSave()
beforeDelete()
afterDelete()
configureFieldable()
bindFieldable()
unbindFieldable()
attachEntityFields()
trigger()
eventDispatcher()
triggered()
addColumn()
$Values
$Attributes
No constants found
_validation()
_attributesForEntity()
_fetchPost()
_prepareMockField()
_resolveBundle()
_scopeQuery()
_virtualQuery()
_columnName()
_attributes()
_initModels()
_getAttributeNames()
_getEntityId()
_getType()
_getBundle()
_isSearchable()
_mapType()
$_cache
$_defaultConfig
$_tableAlias
$_schemaColumns
$_attributes
N/A
No private methods found
No private properties found
N/A

Properties

$_cache

$_cache : array

Used for reduce BD queries and allow inter-method communication.

Example, it allows to pass some information from beforeDelete() to afterDelete().

Type

array

$_defaultConfig

$_defaultConfig : array

Default configuration.

Type

array

$_tableAlias

$_tableAlias : string

Table alias.

Type

string

$_schemaColumns

$_schemaColumns : array

List of real column names of table.

Type

array

$_attributes

$_attributes : array

Attributes index by bundle, and by name within each bundle.

[
    'administrator' => [
        'admin-address' => [
            'type' => 'varchar',
            'searchable' => false
        ],
        'admin-phone' => [
            'type' => 'varchar',
            'searchable' => true
        ]
    ],
    'editor' => [
        'editor-last-login' => [
            'type' => 'datetime',
            'searchable' => false,
        ]
    ]
]

Type

array

Methods

__construct()

__construct(\Cake\ORM\Table $table, array $config)

Constructor.

Parameters

\Cake\ORM\Table $table

The table this behavior is attached to

array $config

Configuration array for this behavior

implementedEvents()

implementedEvents() : void

Returns a list of events this class is implementing. When the class is registered in an event manager, each individual method will be associated with the respective event.

beforeFind()

beforeFind(\Cake\Event\Event $event, \Cake\ORM\Query $query, \ArrayObject $options, boolean $primary) : boolean|null

Attaches virtual properties to entities.

This method iterates over each retrieved entity and invokes the attachEntityAttributes() method. This method should return the altered entity object with its virtual properties, however if this method returns FALSE the entity will be removed from the resulting collection. And if this method returns NULL will stop the find() operation.

This method is also responsible of looking for virtual columns in WHERE clause (if applicable) and properly scope the Query object. Query scoping is performed by the _scopeQuery() method.

Parameters

\Cake\Event\Event $event

The beforeFind event that was triggered

\Cake\ORM\Query $query

The original query to modify

\ArrayObject $options

Additional options given as an array

boolean $primary

Whether this find is a primary query or not

Returns

boolean|null

attachEntityAttributes()

attachEntityAttributes(\Cake\Datasource\EntityInterface $entity, array $options) : boolean|null|\Cake\Datasource\EntityInterface

The method which actually fetches custom fields, invoked by `beforeFind()` for each entity in the collection.

  • Returning FALSE indicates the entity should be removed from the resulting collection.

  • Returning NULL will stop the entire find() operation.

Parameters

\Cake\Datasource\EntityInterface $entity

The entity where to fetch fields

array $options

Arguments given to beforeFind() method, possible keys are "event", "query", "options", "primary"

Returns

boolean|null|\Cake\Datasource\EntityInterface

beforeSave()

beforeSave(\Cake\Event\Event $event, \Cake\Datasource\EntityInterface $entity, array $options) : boolean

Before an entity is saved.

Events Triggered:

  • Field.<FieldHandler>.Entity.validate: It receives three arguments, the field entity representing the field being saved, an options array and a Validator object. The options array is passed as an ArrayObject, so any changes in it will be reflected in every listener and remembered at the end of the event so it can be used for the rest of the save operation. The validator object should be altered by adding rules that will be used later to validate the given field entity, this validator object is used exclusively to validate the given field entity.

  • Field.<FieldHandler>.Entity.beforeSave: It receives two arguments, the field entity representing the field being saved and options array. The options array is passed as an ArrayObject, so any changes in it will be reflected in every listener and remembered at the end of the event so it can be used for the rest of the save operation. Returning false in any of the Field Handler will abort the saving process. If the Field event is stopped using the event API, the Field event object's result property will be returned.

Here is where we dispatch each custom field's $_POST information to its corresponding Field Handler, so they can operate over their values.

Fields Handler's Field.<FieldHandler>.Entity.beforeSave event is triggered over each attached field for this entity, so you should have a listener like:

class TextField implements EventListenerInterface
{
    public function implementedEvents()
    {
        return [
            'Field.TextField.Entity.beforeSave' => 'entityBeforeSave',
        ];
    }

    public function entityBeforeSave(Event $event, $entity, $field, $options)
    {
         // alter $field, and do nifty things with $options['_post']
         // return FALSE; will halt the operation
    }
}

You will see $options array contains the POST information user just sent when pressing form submit button:

// $_POST information for this [entity, field_instance] tuple.
$options['_post']

Field Handlers should alter $field->value and $field->extra according to its needs using $options['_post'].

NOTE: Returning boolean FALSE will halt the whole Entity's save operation.

Parameters

\Cake\Event\Event $event

The event that was triggered

\Cake\Datasource\EntityInterface $entity

The entity being saved

array $options

Additional options given as an array

Throws

\Cake\Error\FatalErrorException

When using this behavior in non-atomic mode

Returns

boolean —

True if save operation should continue

afterSave()

afterSave(\Cake\Event\Event $event, \Cake\Datasource\EntityInterface $entity, array $options) : void

After an entity is saved.

Parameters

\Cake\Event\Event $event

The event that was triggered

\Cake\Datasource\EntityInterface $entity

The entity that was saved

array $options

Additional options given as an array

beforeDelete()

beforeDelete(\Cake\Event\Event $event, \Cake\Datasource\EntityInterface $entity, array $options) : boolean

Deletes an entity from a fieldable table.

Events Triggered:

  • Field.<FieldHandler>.Entity.beforeDelete: Fired before the delete occurs. If stopped the delete will be aborted. Receives as arguments the field entity and options array.

Parameters

\Cake\Event\Event $event

The event that was triggered

\Cake\Datasource\EntityInterface $entity

The entity being deleted

array $options

Additional options given as an array

Throws

\Cake\Error\FatalErrorException

When using this behavior in non-atomic mode

Returns

boolean

afterDelete()

afterDelete(\Cake\Event\Event $event, \Cake\Datasource\EntityInterface $entity, array $options) : void

After an entity was removed from database. Here is when EAV values are removed from DB.

Parameters

\Cake\Event\Event $event

The event that was triggered

\Cake\Datasource\EntityInterface $entity

The entity that was deleted

array $options

Additional options given as an array

Throws

\Cake\Error\FatalErrorException

When using this behavior in non-atomic mode

configureFieldable()

configureFieldable(array $config) : void

Changes behavior's configuration parameters on the fly.

Parameters

array $config

Configuration parameters as key => value

bindFieldable()

bindFieldable() : void

Enables this behavior.

unbindFieldable()

unbindFieldable() : void

Disables this behavior.

attachEntityFields()

attachEntityFields(\Cake\Datasource\EntityInterface $entity) : \Cake\Datasource\EntityInterface

The method which actually fetches custom fields.

Fetches all Entity's fields under the _fields property.

Parameters

\Cake\Datasource\EntityInterface $entity

The entity where to fetch fields

Returns

\Cake\Datasource\EntityInterface

trigger()

trigger(array|string $eventName) : \Cake\Event\Event

Triggers the given event name. This method provides a shortcut for:

$this->trigger('EventName', $arg1, $arg2, ..., $argn);

You can provide a subject to use by passing an array as first arguments where the first element is the event name and the second one is the subject, if no subject is given $this will be used by default:

$this->trigger(['GetTime', new MySubject()], $arg_0, $arg_1, ..., $arg_n);

You can also indicate an EventDispatcher instance to use by prefixing the event name with <InstanceName>::, for instance:

$this->trigger('Blog::EventName', $arg1, $arg2, ..., $argn);

This will use the EventDispacher instance named Blog and will trigger the event EventName within that instance.

Parameters

array|string $eventName

The event name to trigger

Returns

\Cake\Event\Event —

The event object that was fired

eventDispatcher()

eventDispatcher(string $name) : \QuickApps\Event\EventDispatcher

Gets an instance of the given Event Dispatcher name.

Usage:

$this->eventDispatcher('myDispatcher')
    ->trigger('MyEventName', $argument)
    ->result;

Parameters

string $name

Name of the dispatcher to get, defaults to 'default'

Returns

\QuickApps\Event\EventDispatcher

triggered()

triggered(string|null $eventName) : integer|array

Retrieves the number of times an event was triggered, or the complete list of events that were triggered.

Parameters

string|null $eventName

The name of the event, if null returns the entire list of event that were fired

Returns

integer|array

addColumn()

addColumn(string $name, array $options) : boolean

Registers a new EAV column or update if already exists.

Usage:

$this->Users->addColumn('user-age', [
    'type' => 'integer',
    'bundle' => 'some-bundle-name',
    'extra' => [
        'option1' => 'value1'
    ]
]);

Parameters

string $name

Column name. e.g. user-age

array $options

Column configuration options

Throws

\Cake\Error\FatalErrorException

When provided column name collides with existing column names

Returns

boolean —

True on success

_validation()

_validation(\Cake\Datasource\EntityInterface $entity) : boolean

Triggers before/after validate events.

Parameters

\Cake\Datasource\EntityInterface $entity

The entity being validated

Returns

boolean —

True if save operation should continue, false otherwise

_attributesForEntity()

_attributesForEntity(\Cake\Datasource\EntityInterface $entity) : array

Gets all attributes that should be attached to the given entity, this entity will be used as context to calculate the proper bundle.

Parameters

\Cake\Datasource\EntityInterface $entity

Entity context

Returns

array

_fetchPost()

_fetchPost(\Field\Model\Entity\Field $field) : mixed

Alters the given $field and fetches incoming POST data, both "value" and "extra" property will be automatically filled for the given $field entity.

Parameters

\Field\Model\Entity\Field $field

The field entity for which fetch POST information

Returns

mixed —

Raw POST information

_prepareMockField()

_prepareMockField(\Cake\Datasource\EntityInterface $entity, \Cake\Datasource\EntityInterface $attribute) : \Field\Model\Entity\Field

Creates a new Virtual "Field" to be attached to the given entity.

This mock Field represents a new property (table column) of the entity.

Parameters

\Cake\Datasource\EntityInterface $entity

The entity where the generated virtual field will be attached

\Cake\Datasource\EntityInterface $attribute

The attribute where to get the information when creating the mock field.

Returns

\Field\Model\Entity\Field

_resolveBundle()

_resolveBundle(\Cake\Datasource\EntityInterface $entity) : string

Resolves `bundle` name using $entity as context.

Parameters

\Cake\Datasource\EntityInterface $entity

Entity to use as context when resolving bundle

Returns

string —

Bundle name as string value, it may be an empty string if no bundle should be applied

_scopeQuery()

_scopeQuery(\Cake\ORM\Query $query, string|null $bundle) : \Cake\ORM\Query

Look for virtual columns in query's WHERE clause.

Parameters

\Cake\ORM\Query $query

The query to scope

string|null $bundle

Consider attributes only for a specific bundle

Returns

\Cake\ORM\Query —

The modified query object

_virtualQuery()

_virtualQuery(\Cake\Database\Expression\Comparison $expression, string|null $bundle) : \Cake\ORM\Query|boolean

Creates a sub-query for matching virtual fields.

Parameters

\Cake\Database\Expression\Comparison $expression

Expression to scope

string|null $bundle

Consider attributes only for a specific bundle

Returns

\Cake\ORM\Query|boolean —

False if not virtual field was found, or search feature was disabled for this field. The query object to use otherwise

_columnName()

_columnName(string $column) : string

Gets a clean column name from query expression.

Example:

$this->_columnName('Tablename.some_column');
// returns "some_column"

$this->_columnName('my_column');
// returns "my_column"

Parameters

string $column

Column name from query

Returns

string

_attributes()

_attributes(string|null $bundle) : array

Gets all attributes added to this table.

Parameters

string|null $bundle

Get attributes within given bundle, or all of them regardless of the bundle if not provided

Returns

array

_initModels()

_initModels() : void

Initializes Values and Attributes tables if there were not set before.

_getAttributeNames()

_getAttributeNames(string $bundle) : array

Gets a list of attribute names.

Parameters

string $bundle

Filter by bundle name

Returns

array

_getEntityId()

_getEntityId(\Cake\Datasource\EntityInterface $entity) : string

Calculates entity's primary key.

If PK is composed of multiple columns they will be merged with : symbol. For example, consider Users table with composed PK <nick, email>, then for certain User entity this method could return:

john-locke:john@the-island.com

Parameters

\Cake\Datasource\EntityInterface $entity

The entity

Returns

string

_getType()

_getType(string $attrName) : string

Gets attribute's EAV type.

Parameters

string $attrName

Attribute name

Returns

string —

Attribute's EAV type

_getBundle()

_getBundle(string $attrName) : string|null

Gets attribute's bundle.

Parameters

string $attrName

Attribute name

Returns

string|null

_isSearchable()

_isSearchable(string $attrName) : boolean

Whether the given attribute can be used in WHERE clauses.

Parameters

string $attrName

Attribute name

Returns

boolean

_mapType()

_mapType(string $type) : string

Maps schema data types to EAV's supported types.

  • datetime: "date", "time", "datetime"
  • decimal: "dec", "float", "decimal"
  • int: "integer", "int", "bool", "boolean"
  • text: "text"
  • varchar: "string", "varchar", "char"

Parameters

string $type

A schema type. e.g. "string", "integer"

Returns

string —

A EAV type. Possible values are datetime, decimal, int, text or varchar