Access Control
Add role based permissions to your documents.
The Access Control extension allows you to set user access permissions on a document with simple annotations.
Configuration
Access Control has no configuration options. Just use:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.accessControl' => true ], ... ]);
However, Access Control requires a configured user
service which is an instance of Zoop\Common\User\RoleAwareUserInterface
. See User Config
Annotations
@Shard\AccessControl
The @Shard\AccessControl
annotation is a document annotation that can contain a list of permission annotations which define who can access the document and what level of access they have. Eg:
/** * @ODM\Document * @Shard\AccessControl({ * ... * }) */ class MyDoc {...}
@Shard\Permission\Basic
The Access Control extension provides the Basic permission. (Other extensions may provide other kinds of permission.) The Basic permission has three arguments:
Name | type | default | description |
---|---|---|---|
allow | string | array | null | A role, or array of roles that are allowed. |
roles | string | array | null | A role, or array of roles that the permission applies to. |
allow | string | array | null | An action, or array of actions that are allowed. |
deny | string | array | null | An action, or array of actions that are denied. |
Access Control defines four actions (other extensions may define further actions):
Name | description |
---|---|
create | Persist a new instance of the document to the database. |
read | Read documents of this type from the database. |
update::$field | Change fields on an instance of this document which has already been persisted. |
delete | Perminently delete this type of document from the database. |
So, for example, the following annotations would allow users with the guest
permission to create
, and read
.
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="guest", allow={"create", "read"}) * }) */ class Simple {...}
Events
Most access control checks happen during a $documentManager->flush()
. Therefore, when an access control check fails, an exception is not raised, as that would prevent a flush from completing correctly. Rather, an event is raised. The following events may be listened to:
Name | description |
---|---|
createDenied | Fires if create is attempted and denied. |
updateDenied | Fires if update is attempted and denied. |
deleteDenied | Fires if delete is attempted and denied. |
Note: there is no event for a rejected read
. This is because read access control is achieved through query filters, meaning both Doctrine and Shard are unaware if, or how many documents may have been filtered out by read access control.
Default permission
If a permission is not allowed, then it is always denied.
So, in this example there isn't any user who can update
or delete
.
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="guest", allow={"create", "read"}) * }) */ class Simple {...}
The * wildcard
The *
can be used to glob role or action names.
In this example all users are allowed to read, editors are also allowed to create, and admins can do everything.
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="*", allow="read"), * @Shard\Permission\Basic(roles="editor", allow="create"), * @Shard\Permission\Basic(roles="admin", allow="*") * }) */ class Simple {...}
Specific actions take precidence over wild cards in the same Permission. Eg, editors are allowed to all actions except delete
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="editor", allow="*", deny="delete") * }) */ class Simple {...}
Order of permissions
Permissions are read in the order they are listed, so permissions lower on the list can override permissions higher on the list. Eg, if a user with the role editor
tries to create
they will be allowed, because the later permission overrides the earlier permission.
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles={"guest", "editor"}, allow="read"), * @Shard\Permission\Basic(roles="editor", allow="create") * }) */ class Simple {...}
Users with multiple roles
Users may have more than one role. Eg, if a user has only the editor role, they they will be allowed to create, but not be allowed to read. If a user has both the guest and editor role, they will be allowed to read and create.
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="guest", allow="read"), * @Shard\Permission\Basic(roles="editor", allow="create") * }) */ class Simple {...}
Update actions
Update actions are related to individual fields, not whole documents. To allow all fields to be updated, use the *
wildcard. Eg:
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="editor", allow="update::*") * }) */ class Simple {...}
To allow update on a specific field, use the field name. Eg:
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="editor", allow="update::description") * }) */ class Simple {...}
To allow update on all fields, except some:
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="editor", allow="update::*", deny={"update::lockedField1", "update::lockedField2"}) * }) */ class Simple {...}
Access Controller Service
Use the Access Controller service's areAllowed
method to check if the configured user has permission to do actions on a document.
AccessController::areAllowed arguments
Name | type | description |
---|---|---|
actions | array | An array of action names to check. |
metadata | ClassMetadata | The metadata for the document type being checked. This argument doesn't have to be passed, but is required if checking the create action, because no document instance exists before it is created. |
document | Object | A document instance to check permissions against. Not required when checking create action. |
This method will return an AllowedResult object.
For example:
$accessController = $manifest->getServiceManager->get('accessController'); if ( ! $accessController->areAllowed('update::name', null, $mydocument)->getAllowed()){ //configured user is not allowed to update the name field of $mydocument; }
Annotations
Event based annotation handling.
The Annotation Extension is a low level extension that handles reading all the @Shard
annotations. It is automatically enabled by any extension that requires annotations, so it can normally be ignored.
Crypt
Hash or encrypt document fields.
Configuration
Access Control has no configuration options. Just use:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.crypt' => true ], ... ]);
Hash
A hash is a one way encryption method. That is, once the text is encrypted, you can't get the plain text back. It is especially useful for user passwords.
@Shard\Crypt\Hash
To hash a field, just add the @Shard\Crypt\Hash
annotation to that field. Eg:
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; class MyDocument { /** * @ODM\String * @Shard\Crypt\Hash */ protected $password; ... }
Hash Salt
It is wise to use a salt when hashing to make cracking the encryption harder.
Salt stored in Document
By default, the crypt extension will check if your document implements Zoop\Common\Crypt\SaltInterface
. If so, that will be used to retireve a salt. Eg:
use Zoop\Common\Crypt\SaltInterface; use Zoop\Shard\Crypt\SaltGenerator; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; class MyDocument implements SaltInterface { /** * @ODM\String * @Shard\Crypt\Hash */ protected $password; /** * @ODM\String */ protected $salt; public function getSalt(){ if (!isset($this->salt)){ $this->salt = SaltGenerator::generateSalt(); } return $this->salt; } ... }
Note: if there are several hashed fields in the one document, this method will use the same salt for all.
Alternate salt
If you want to use a different salt, set the salt
property of the annotation to a service name that will return an instance of Zoop\Common\Crypt\SaltInterface
. Eg
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; class MyDocument { /** * @ODM\String * @Shard\Crypt\Hash(salt='mysaltservice') */ protected $password; ... }
And configure your salt service in the Manifest:
$manifest = new Zoop\Shard\Manifest([ 'service_manager_config' => [ 'invokables' => [ 'mysaltservice' => 'My\Salt' //A class that implements SaltInterface ] ] ]);
Alternate Hash Algorithim
The default hash algorithim is in Zoop\Shard\Crypt\Hash\BasicHashService
. If you would like to use an alternate hashing algorithim, set the service
property of the annotation to a service name that will return an instance of Zoop\Shard\Crypt\Hash\HashServiceInterface
. Eg:
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; class MyDocument { /** * @ODM\String * @Shard\Crypt\Hash(service='myhashservice') */ protected $password; ... }
And configure your hash service in the Manifest:
$manifest = new Zoop\Shard\Manifest([ 'service_manager_config' => [ 'invokables' => [ 'myhashservice' => 'My\Hash\Service' //A class that implements HashServiceInterface ] ] ]);
Block Cipher
A block cipher is a two way encryption method. That is, once the text is encrypted, you can get the plain text back.
@Shard\Crypt\BlockCipher
To encrypt a field, just add the @Shard\Crypt\BlockCipher
annotation to that field. You must also set the name of a key service. Eg:
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; class MyDocument { /** * @ODM\String * @Shard\Crypt\BlockCipher(key="mykey") */ protected $password; ... }
Key Service
The key is used to encrypt and decrypt the field. Keep your keys safe! If someone steals your keys, then they can unlock your data.. The key is set with a key service which must return an instance of Zoop\Common\Crypt\KeyInterface
. Eg:
$manifest = new Zoop\Shard\Manifest([ 'service_manager_config' => [ 'invokables' => [ 'mykey' => 'My\Key' //A class that implements KeyInterface ] ] ]);
use Zoop\Common\Crypt\KeyInterface; class Key implements KeyInterface { public function getKey() { return 'my very secret key phrase'; } }
Block Cipher Salt
If you want to use a salt with the Block Cipher, you can.
Salt stored in Document
By default, the crypt extension will check if your document implements Zoop\Common\Crypt\SaltInterface
. If so, that will be used to retireve a salt. Eg:
use Zoop\Common\Crypt\SaltInterface; use Zoop\Shard\Crypt\SaltGenerator; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; class MyDocument implements SaltInterface { /** * @ODM\String * @Shard\Crypt\Hash */ protected $password; /** * @ODM\String */ protected $salt; public function getSalt(){ if (!isset($this->salt)){ $this->salt = SaltGenerator::generateSalt(); } return $this->salt; } ... }
Note: if there are several encrypted fields in the one document, this method will use the same salt for all.
Alternate salt
If you want to use a different salt, set the salt
property of the annotation to a service name that will return an instance of Zoop\Common\Crypt\SaltInterface
. Eg
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; class MyDocument { /** * @ODM\String * @Shard\Crypt\BlockCypter(key='mykey', salt='mysaltservice') */ protected $password; ... }
And configure your salt service in the Manifest:
$manifest = new Zoop\Shard\Manifest([ 'service_manager_config' => [ 'invokables' => [ 'mykey' => 'My\Key', 'mysaltservice' => 'My\Salt' //A class that implements SaltInterface ] ] ]);
Alternate Encryption Algorithim
The default block cypher algorithim is in Zoop\Shard\Crypt\BlockCypher\ZendBlockCypherService
. If you would like to use an alternate algorithim, set the service
property of the annotation to a service name that will return an instance of Zoop\Shard\Crypt\BlockCypher\BlockCypherServiceInterface
. Eg:
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; class MyDocument { /** * @ODM\String * @Shard\Crypt\BlockCypher(key="mykey", service='mycipherservice') */ protected $password; ... }
And configure your service in the Manifest:
$manifest = new Zoop\Shard\Manifest([ 'service_manager_config' => [ 'invokables' => [ 'mykey' => 'My\Key', 'mycipherservice' => 'My\Cipher\Service' //A class that implements BlockCipherServiceInterface ] ] ]);
Freeze
Freeze documents against updating or deleting.
Configuration
Freeze has no configuration options. Just use:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.freeze' => true ], ... ]);
Making a document freezable
To make a document freezable, a boolean field should be annotated with @Shard\Freeze
. Eg:
/** * @ODM\Boolean * @Shard\Freeze */ protected $frozen = false;
For convienence you can use the Zoop\Shard\Freeze\DataModel\FreezableTrait
to add such a field to a document. Eg:
use Zoop\Shard\Freeze\DataModel\FreezeableTrait; //Annotation imports use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** @ODM\Document */ class MyDocument { use FreezeableTrait; ... }
Using the Freezer service
The freezer can be used to freeze and thaw documents. When frozen they cannot be updated or deleted. Note that the frozen state is not persisted until the DocumentManager is flushed. Eg:
$freezer = $manifest->getServiceManager()->get('freezer'); //get the freezer service $freezer->freeze($myDocument); //freeze a document $freezer->thaw($anotherDocument); //thaw a document $manifest->getServiceManager()->get('mydocumentmanager')->flush() //flush to persist changes
Freeze and Thaw stamps
Timestamps
The freeze extension support automatic timestamping of freeze and thaw events. Use the @Shard\Freeze\FrozenOn
and @Shard\Freeze\ThawedOn
annotations. Eg:
/** * @ODM\Timestamp * @Shard\Freeze\FrozenOn */ protected $frozenOn; /** * @ODM\Timestamp * @Shard\Freeze\ThawedOn */ protected $thawedOn;
Alternately you can use traits. Eg
use Zoop\Shard\Freeze\DataModel\FreezeableTrait; use Zoop\Shard\Freeze\DataModel\FreezenOnTrait; use Zoop\Shard\Freeze\DataModel\ThawedOnTrait; //Annotation imports use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** @ODM\Document */ class MyDocument { use FreezeableTrait; use FrozenOnTrait; use ThawedOnTrait; ... }
The values of the fields can be retrieved with:
$myDocument->getFrozenOn(); $myDocument->getThawedOn();
User stamps
The freeze extension support automatic stamping with the active username on freeze and thaw events. Use the @Shard\Freeze\FrozenBy
and @Shard\Freeze\ThawedBy
annotations. This requires a configured user. Eg:
/** * @ODM\String * @Shard\Freeze\FrozenBy */ protected $frozenBy; /** * @ODM\String * @Shard\Freeze\ThawedBy */ protected $thawedBy;
Alternately you can use traits. Eg
use Zoop\Shard\Freeze\DataModel\FreezeableTrait; use Zoop\Shard\Freeze\DataModel\FreezenByTrait; use Zoop\Shard\Freeze\DataModel\ThawedByTrait; //Annotation imports use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** @ODM\Document */ class MyDocument { use FreezeableTrait; use FrozenByTrait; use ThawedByTrait; ... }
The values of the fields can be retrieved with:
$myDocument->getFrozenBy(); $myDocument->getThawedBy();
Access Conntrol
The Freeze extension can hook into the Access Control extension to allow or deny roles to the freeze
and thaw
actions. This requires the Access Control extension to be enabled, as well as the Freeze extension. Eg:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.accessControl' => true, 'extension.freeze' => true ], ... ]);
Permissions can then be used as normal with the added actions of freeze
and thaw
. Eg:
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="editor", allow="freeze", deny="thaw") * ... * }) */ class Simple {...}
Freeze Filter
The freeze extension provides a filter that can be used to remove frozen documents from result sets.
To filter out all frozen documents, use:
$documentManager->getFilterCollection()->enable('freeze');
To filter so only frozen documents are returned use:
$documentManager->getFilterCollection()->enable('freeze'); $filter = $documentManager->getFilterCollection()->getFilter('freeze'); $filter->onlyFrozen();
Events
Freeze provides the following events which can be subscribed to with the Doctrine EventManager:
Name | description |
---|---|
preFreeze | Fires before freeze happens. |
postFreeze | Fires after freeze happens. |
preThaw | Fires before thaw happens. |
postThaw | Firest after thaw happens. |
freezeDenied | Fires if a freeze is attempted but denied by access control. |
thawDenied | Fires if a thaw is attempted by denied by access control. |
frozenUpdateDenied | Fires if attempt is made to update a frozen document. |
frozenDeleteDenied | Fires if attempt is made to delete a frozen document. |
Generator
Dynamically generate new resources based on document classes
The generator extension can be used to dynamically generate resources such as help files, javascript, and api documentation, from your document classes.
Configuration
Configuration is very important for the Generator extension. A resource_map
must be defined. A resource_map
is an array of resources that can be generated by the generator extension. The array keys are the resource names.
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.generator' => [ 'my resource name' => [ /* resource generator config goes here */ ] ] ], ... ]);
The configuration array for a resource may have three keys:
Name | type | required | description |
---|---|---|---|
generator | string | true | The service name which will return an instance of Zoop\Shard\Generator\GeneratorInterface |
class | string | true | The class name of a document class that will be used to generate the resource. |
options | array | false | An array of options that will be passed to the generator. |
A complete config might look like:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.generator' => [ 'userDescription.html' => [ 'generator' => 'my.description.generator', 'class' => 'My\Documents\User', 'options' => [ 'theme' => 'ocean_blue' ] ] ] ], ... ]);
The Resource Map
The resource map is a service provided by the generator extension. To get it use:
$resourceMap = $manifest->getServiceManager()->get('resourceMap');
To check if a resource can be generated:
if ($resourceMap->has('userDescription.html')){ //resource can be generated } else { //resource can't be generated };
To generate a resource:
$html = $resourceMap->get('userDescription.html');
Create your own generator
To create your own generator, just implement Zoop\Shard\Generator\GeneratorInterface
. Eg:
namespace My; use Zoop\Shard\Generator\GeneratorInterface; class DescriptionGenerator implements GeneratorInterface { public function generate($name, $class, $options = null) { return "You requested resource $name for $class be generated
"; } }
You will also need to register your generator with the service manager. This is most easily done when configuring the Manifest. Eg:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.generator' => [ ... ] ], 'service_manager_config' => [ 'invokables' => [ 'my.description.generator' => 'My\DescriptionGenerator' ], ... ] ]);
Owner
Add an owner field to documents, and enable the owner based access control.
Configuration
Owner has no configuration options. Just use:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.owner' => true ], ... ]);
However, the Owner extension requires a configured user
service which is an instance of Zoop\Common\User\UserInterface
. See User Config
Add an owner field
Place the @Shard\Owner
annotation on a field.
/** * @ODM\String * @Shard\Owner */ protected $owner;
Alternately you can use traits. Eg
use Zoop\Shard\Owner\DataModel\OwnerTrait; //Annotation imports use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** @ODM\Document */ class MyDocument { use OwnerTrait; ... }
The values of the field can be set and retrieved with:
$myDocument->setOwner(); $myDocument->getOwner();
Note: the value of the owner field is not automatically assigned to the active use when a document is created. It must be manually set.
Owner based access control
If the Access Control extension is enabled along with the Owner extension, the owner
role can be used to allow or deny actions if the current user is equal to the owner field.
For example, the following access control annotations, only the owning user may read the document.
/** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="owner", allow="read") * }) */
Access control owner field update
It is normally required to access control the ability to change who owns a document. Eg:
/** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="*", allow={"create", "read"} ), * @Shard\Permission\Basic(roles="owner", allow="update::*", deny="update::owner"), * @Shard\Permission\Basic(roles="admin", allow="update::owner" ) * }) */
In this example, all roles can read. Only the own can update a document. And only an admin can update the document owner.
Serializer
Serialize document instances to array or json, and unserialize back to document instances.
The shard Serializer offers fine grained control over how documents are serialized and unserialized, with particluar mind to ajax and web clients.
Configuration
The serializer does not require any specific configuration.
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.serializer' => true ], ... ]);
However, some configuration options are available:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.serializer' => [ 'type_serializers' => [...] 'max_nesting_depth' => [...] ] ], ... ]);
Name | type | default | description |
---|---|---|---|
type_serializer | array | ['date' => 'serializer.type.dateToISO8601'] | An array of type serializers. For more information see below. |
max_nesting_depth | integer | 1 | How deeply should the tree of references inside documents be followed when serializing. |
Serializing Documents
First get the Serializer service, and then it can be used to serialize documents:
$serializer = $manifest->getServiceManager()->get('serializer'); $array = $serializer->toArray($myDocument); //serialize to array $json = $serializer->toJson($myDocument); //serialize to json
@Shard\Serializer\Ignore and @Shard\Unserializer\Ignore
Place on a document field to control if the field is serialized
Always ignore the field, eg:
/** * @ODM\String */ protected MyProperty; /* or */ /** * @ODM\String * @Shard\Serializer\Ignore * @Shard\Unserializer\Ignore */ protected MyProperty;
Ignore a field only when serializing (not when unserializing):
/** * @ODM\String * @Shard\Serializer\Ignore */ protected MyProperty;
Ignore a field only when unserializing (not when serializing):
/** * @ODM\String * @Shard\Unserializer\Ignore */ protected MyProperty;
Reference Serializers
Document references can be serialized in several differnet ways.
RefLazy
By default references will be serialized to an array like this:
[$ref: 'CollectionName/DocumentId']
The $ref
style of referencing is what Mongo uses internally.
The default behaviour uses the RefLazy serializer. However this can be overridden by defineing an alternative ReferenceSerializer as a property annotation.
Two alternate ReferenceSerializers are already included with Shard.
SimpleLazy
SimpleLazy will serialize a reference as the mongo id. It can be used like this:
/** * @ODM\ReferenceMany(targetDocument="MyTargetDocument") * @Sds\Serializer\SimpleLazy */ protected $myDocumentProperty;
Eager
Eager will serialize references as if they were embedded documents. It can be used like this:
/** * @ODM\ReferenceMany(targetDocument="MyTargetDocument") * @Sds\Serializer\Eager */ protected $myDocumentProperty;
When using the Eager serializer, the maxNestingDepth configuration option will control how deep the Eager serializer will go into a tree of references.
Custom Reference Serializer
You can create your own reference serializer to render references however you like. To do so, implement the Shard\Serializer\Reference\ReferenceSerializerInterface
.
Then register your serializer with the service manager in the manifest config:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.serializer' => [ ... ] ], 'service_manager_config' => [ 'invokables' => [ 'my.reference.serializer' => 'My\ReferenceSerializer\Class' ], ... ] ]);
To use your reference serializer, use the annotation:
/** * @ODM\ReferenceMany(targetDocument="MyTargetDocument") * @Sds\Serializer\ReferenceSerializer("my.reference.serializer") */ protected $myDocumentProperty;
Date Serializer
Fields of type date
are serialized by default with Shard\Serializer\Type\DateToISO8601
.
To override this format, see Custom Type Serializers below
Custom Type Serializers
Each document field has an associated type, such as string or date. Serialization may be customized by type.
First create a class which implements the Shard\Serializer\Type\TypeSerializerInterface
. You will need to define serialize and unserialize methods.
For example, this class will uppercase the first letter of every string when serializing, and lower case the first letter when unserializing:
use Shard\Serializer\Type\TypeSerializerInterface; class MyStringSerializer implements TypeSerializerInterface { public static function serialize($value) { return ucfirst($value); } public static function unserialize($value) { return lcfirst($value); } }
Then the class needs to be registered in the extension config and service manager:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.serializer' => [ 'type_serializers' => [ 'string' => 'my.string.serializer' ] ] ], 'service_manager_config' => [ 'invokables' => [ 'my.string.serializer' => 'MyStringSerializer' ], ... ] ]);
The default Date serializer is an example of a Type Serializer which is regisered by default. To over ride it, simply register your own in the extension config.
Unserializing Documents
Get the unserializer from the service manager:
$unserializer = $manifest->getServiceManager()->get('unserializer');
Both fromArray
and fromJson
will unserialize an array or json into a document. They take up to four arguments:
Name | type | default | description |
---|---|---|---|
data | array | string | The data to be unserialized. An array for fromArray , or a json string for fromJson . |
|
className | string | null | The class name of the document instance to create. |
document | object | null | If supplied, the unserializer won't attempt to load any document from the db, but use this one instead. |
mode | string | unserialize_patch | Must be either unserialize_update or unserialize_patch . If unserialize_update is used, the unserialized document will replace any existing document in the db with the same id. If unserialize_patch is used the unserialized data will be merged with any existing document in the db. |
Soft Delete
Mark documents as deleted, without actually deleting them.
Soft Deleted documents are simply marked as soft deleted, so they can be filtered out from result sets. Soft Deleted documents cannot be updated. However, note that Soft Deleted documents can still be fully deleted. If you need to control delete access, then use the Access Control extension.
Configuration
Soft Delete has no configuration options. Just use:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.softDelete' => true ], ... ]);
Making a document soft deletable
To make a document soft deleteable, a boolean field should be annotated with @Shard\SoftDelete
. Eg:
/** * @ODM\Boolean * @Shard\SoftDelete */ protected $softDeleted = false;
For convienence you can use the Zoop\Shard\SoftDelete\DataModel\SoftDeletableTrait
to add such a field to a document. Eg:
use Zoop\Shard\SoftDelete\DataModel\SoftDeletableTrait; //Annotation imports use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** @ODM\Document */ class MyDocument { use SoftDeletableTrait; ... }
Using the SoftDeleter service
The SoftDeleter can be used to soft delete and restore documents. Note that the soft delete state is not persisted until the DocumentManager is flushed. Eg:
$softDeleter = $manifest->getServiceManager()->get('softDeleter'); //get the softDeleter service $softDeleter->softDelete($myDocument); //soft delete a document $softDeleter->restore($anotherDocument); //restore a document $manifest->getServiceManager()->get('mydocumentmanager')->flush() //flush to persist changes
Soft Delete and Restore stamps
Timestamps
The soft delete extension supports automatic timestamping of soft delete and restore events. Use the @Shard\SoftDelete\SoftDeletedOn
and @Shard\SoftDelete\RestoredOn
annotations. Eg:
/** * @ODM\Timestamp * @Shard\SoftDelete\SoftDeletedOn */ protected $softDeletedOn; /** * @ODM\Timestamp * @Shard\SoftDelete\RestoredOn */ protected $restoredOn;
Alternately you can use traits. Eg
use Zoop\Shard\SoftDelete\DataModel\SoftDeletableTrait; use Zoop\Shard\SoftDelete\DataModel\SoftDeletedOnTrait; use Zoop\Shard\SoftDelete\DataModel\RestoredOnTrait; //Annotation imports use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** @ODM\Document */ class MyDocument { use SoftDeletableTrait; use SoftDeletedOnTrait; use RestoredOnTrait; ... }
The values of the fields can be retrieved with:
$myDocument->getSoftDeletedOn(); $myDocument->getRestoredOn();
User stamps
The soft delete extension supports automatic stamping with the active username on soft delete and restore events. Use the @Shard\SoftDelete\SoftDeletedBy
and @Shard\SoftDelete\RestoredBy
annotations. This requires a configured user. Eg:
/** * @ODM\String * @Shard\SoftDelete\SoftDeletedBy */ protected $softDeletedBy; /** * @ODM\String * @Shard\SoftDelete\RestoredBy */ protected $restoredBy;
Alternately you can use traits. Eg
use Zoop\Shard\SoftDelete\DataModel\SoftDeleteableTrait; use Zoop\Shard\SoftDelete\DataModel\SoftDeletedByTrait; use Zoop\Shard\SoftDelete\DataModel\RestoredByTrait; //Annotation imports use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** @ODM\Document */ class MyDocument { use SoftDeleteableTrait; use SoftDeletedByTrait; use RestoredByTrait; ... }
The values of the fields can be retrieved with:
$myDocument->getSoftDeletedBy(); $myDocument->getRestoredBy();
Access Conntrol
The soft deleted extension can hook into the Access Control extension to allow or deny roles to the softDelete
and restore
actions. This requires the Access Control extension to be enabled, as well as the Soft Delete extension. Eg:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.accessControl' => true, 'extension.softDelete' => true ], ... ]);
Permissions can then be used as normal with the added actions of softDelete
and restore
. Eg:
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="editor", allow="softDelete", deny="restore") * ... * }) */ class Simple {...}
Soft Delete Filter
The soft delete extension provides a filter that can be used to remove soft deleted documents from result sets.
To filter out all soft deleted documents, use:
$documentManager->getFilterCollection()->enable('softDelete');
To filter so only soft deleted documents are returned use:
$documentManager->getFilterCollection()->enable('softDelete'); $filter = $documentManager->getFilterCollection()->getFilter('softDelete'); $filter->onlySoftDeleted();
Events
Soft Delete provides the following events which can be subscribed to with the Doctrine EventManager:
Name | description |
---|---|
preSoftDelete | Fires before soft delete happens. |
postSoftDelete | Fires after soft delete happens. |
preRestore | Fires before restore happens. |
postRestore | Firest after restore happens. |
softDeleteDenied | Fires if a soft delete is attempted but denied by access control. |
restoreDenied | Fires if a restore is attempted by denied by access control. |
softDeleteUpdateDenied | Fires if attempt is made to update a soft deleted document. |
Stamp
Stamp documents with creation and update timestamp and user.
Configuration
Stamp has no configuration options. Just use:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.stamp' => true ], ... ]);
Create and Update stamps
Timestamps
The stamp extension supports automatic timestamping of create and update events on documents. Use the @Shard\Stamp\CreatedOn
and @Shard\Stamp\UpdatedOn
annotations. Eg:
/** * @ODM\Timestamp * @Shard\Stamp\CreatedOn */ protected $createdOn; /** * @ODM\Timestamp * @Shard\Stamp\UpdatedOn */ protected $updatedOn;
Alternately you can use traits. Eg
use Zoop\Shard\Stamp\DataModel\CreatedOnTrait; use Zoop\Shard\Stamp\DataModel\CreatedOnTrait; //Annotation imports use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** @ODM\Document */ class MyDocument { use CreatedOnTrait; use UpdatedOnTrait; ... }
The values of the fields can be retrieved with:
$myDocument->getCreatedOn(); $myDocument->getUpdatedOn();
User stamps
The stamp extension supports automatic stamping with the active username on document create and update. Use the @Shard\Stamp\CreatedBy
and @Shard\Stamp\UpdatedBy
annotations. This requires a configured user. Eg:
/** * @ODM\String * @Shard\Stamp\CreatedBy */ protected $createdBy; /** * @ODM\String * @Shard\Stamp\UpdatedBy */ protected $updatedBy;
Alternately you can use traits. Eg
use Zoop\Shard\State\DataModel\CreatedByTrait; use Zoop\Shard\State\DataModel\UpdatedByTrait; //Annotation imports use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** @ODM\Document */ class MyDocument { use CreatedByTrait; use UpdatedByTrait; ... }
The values of the fields can be retrieved with:
$myDocument->getCreatedBy(); $myDocument->getUpdatedBy();
Access Conntrol
The stamp extension can hook into the Access Control extension to provide the extra roles of creator
and updater
. Permissions can be allowed or denied if the current active user is the creator or updater. Eg:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.accessControl' => true, 'extension.stamp' => true ], ... ]);
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Zoop\Shard\Annotation\Annotations as Shard; /** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Basic(roles="creator", allow="update::*") * ... * }) */ class Simple {...}
State
Add state to documents and build document workflows.
A document has a state, such as 'draft' and can be transitioned to another state, such as 'published'.
Configuration
State has no configuration options. Just use:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.state' => true ], ... ]);
Adding state to a Document
To add state, add @Shard\State
to a field. Eg:
/** * @ODM\String * @Shard\State */ protected $state;
For convienence you can use the Zoop\Shard\State\DataModel\StateTrait
to add such a field to a document.
State Access Control
Access Control permissions can be tied to document state. Use @Shard\Permission\State
.
Eg. These permissions allow all roles to read only when a document is in a published state. A writer role is allowed to create, read and update documents in a draft state.
/** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\State (roles="*", states="published", allow="read" ), * @Shard\Permission\State (roles="writer", states="draft", allow={"create", "update", "read"}), * }) */ class AccessControlled { ... }
States can be listed as an array, and also support wilds. Eg:
/** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\State (roles="*", states="published", allow="read" ), * @Shard\Permission\State (roles="writer", states={"d*", "published"}, allow={"create", "update", "read"}), * }) */ class AccessControlled { ... }
Transition Access Control
Access Control permission can use used to control who can make changes to document state. Use @Shard\Permission\Transition
Eg. These permissions allow a writer to move a document from draft to reivew. A reviewer may move a document from review to draft or review to published. An admin can make any transition.
/** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Transition(roles="writer", allow="draft->review" ), * @Shard\Permission\Transition(roles="reviewer", allow={"review->draft", "review->published"}), * @Shard\Permission\Basic (roles="admin", allow="*" ) * }) */ class AccessControlled { ... }
Transitions can have wilds. (very handy).
/** * @ODM\Document * @Shard\AccessControl({ * @Shard\Permission\Transition(roles="writer", allow="draft->review"), * @Shard\Permission\Transition(roles="reviewer", allow="review->*" ), * @Shard\Permission\Basic (roles="admin", allow="*" ) * }) */ class AccessControlled { ... }
State Filter
The state extension provides a filter that can be used to filter result sets based on document state.
The state filter takes a list of states, and if those states should be included or excluded.
Eg, exclude some states:
$documentManager->getFilterCollection()->enable('state'); $filter = $documentManager->getFilterCollection()->getFilter('state'); $filter->setStates(['inactive']); $filter->excludeStateList();
Eg, include some states:
$documentManager->getFilterCollection()->enable('state'); $filter = $documentManager->getFilterCollection()->getFilter('state'); $filter->setStates(['published', 'draft']); $filter->includeStateList();
Events
State provides the following events which can be subscribed to with the Doctrine EventManager:
Name | description |
---|---|
preTransition | Fires before state transition happens. |
onTransition | Fires when a transition is happening. That is, after access control checks but before a new document change set is prepared for document persistance. |
postTransition | Fires after a transition has happened. |
transitionDenied | Fires if Access Control has denied permission to make a requested transition. |
Validator
Add validation to documents.
Validate fields, and whole documents so only quality data makes it into your database.
The Validator extensions uses the Mystique validator library.
Configuration
State has no configuration options. Just use:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.validator' => true ], ... ]);
Adding validation to a Field
To add a validator use annotations in the @Shard\Validator
namespace. Eg:
/** * @ODM\String * @Shard\Validator\Length("min" = 5, "max" = 10) */ protected $myproperty;
To add a multiple validators in a chain, use @Shard\Validator\Chain
. Eg:
/** * @ODM\String * @Shard\Validator\Chain({ * @Shard\Validator\Alpha, * @Shard\Validator\Length("min" = 5, "max" = 10) * }) */ protected $myproperty;
All the validators in the Mystique library are supported with their own annotation.
Custom Validators
To write your own validators, inherit from Zoop\Mystique\Base
and use the @Shard\Validator
annotation.
/** * @ODM\String * @Shard\Validator("class" = "My\Validator\Class", "options" = {"constuctor options array"}) */ protected $myproperty;
Document validators
Sometimes you want a validation rule that interrogates more than one field. To do so, just use the @Shard\Validator
annotation on a document, rather than a field. Eg:
/** * @ODM\Document * @Shard\Validator(class = "Zoop\Shard\Test\Validator\TestAsset\ClassValidator") */ class Simple { ... }
The isValid
method of the validator will be passed the complete document instance to validate.
Document Validator Service
To validate a document before flush, use the documentValidator service. This will validate all fields and any document validators. Eg:
$documentValidator = $manifest->getServiceManager()->get('documentValidator'); $result = $documentValidator->isValid($myDocument);
Events
Validator provides the following events which can be subscribed to with the Doctrine EventManager:
Name | description |
---|---|
invalidUpdate | Fires during flush if and updated document is invalid. |
invalidCreate | Fires during flush if and updated document is invalid. |
Zone
Add zones to documents.
A zone is a region of relevance. For example, it may be a company department, a geographical area.
A document may be assigned multiple zones.
Configuration
Zone has no configuration options. Just use:
$manifest = new Zoop\Shard\Manifest([ ... 'extension_configs' => [ 'extension.zone' => true ], ... ]);
Adding zones to a Document
To add zones, add @Shard\Zones
to a field. Eg:
/** * @ODM\Collection * @Shard\Zones */ protected $zones;
For convienence you can use the Zoop\Shard\Zone\DataModel\ZoneTrait
to add such a field to a document.
Zone Filter
The zone extension provides a filter that can be used to filter result sets based on document zones.
The zone filter takes a list of zones, and if those zones should be included or excluded.
Eg, exclude some zones:
$documentManager->getFilterCollection()->enable('zone'); $filter = $documentManager->getFilterCollection()->getFilter('zone'); $filter->setStates(['business-support']); $filter->excludeZoneList();
Eg, include some states:
$documentManager->getFilterCollection()->enable('zone'); $filter = $documentManager->getFilterCollection()->getFilter('zone'); $filter->setStates(['accounts', 'hr']); $filter->includeZoneList();