- upgrade to Yii 2.0.13 dependency : if you are using a version of Yii 2.0.13 or greater you must use this version (or greater).
about Events : Since Yii v2.0.14 it is possible to specify event name as a wildcard pattern
$component->on('event.group.*', function ($event) {
Yii::trace($event->name . ' is triggered.');
});
This may cause a problem if you are using the ExtendedEventSequence
model because it is making use of the wildcard characters to name events (for example : beforeEnterWorkflow(*)). Consequently if you set an event handler for beforeEnterWorkflow(MY_WORKFLOW) event, the handler will be invoked twice : once for event \ beforeEnterWorkflow(MY_WORKFLOW)
and once for event beforeEnterWorkflow(*)
- Add
ChangeStatusAction
(from #24)
- fix #42
- Ci : update components versions
- Ci : remove scrutinizer hook
- Ci : remove php version prior to 5.6
- add unit tests for
WorkflowHelper::getStatusDropDownData()
- refactor User guide and Class Reference documentation
- fix CI failure : deep-copy 1.4.0 caused error
Maximum function nesting level of '200' reached, aborting!
. Version 1.3.1 is ok - add composer.lock
- fix doc blocks
release 1.0.0
- remove unnecessary
use
statement inSimpleWorkflowBehavior
- test against
StatusInterface
and not anymoreStatus
- update doc
- minor change in the
WorkflowFileSource->parseStatusId()
- fix : restore model errors after
getNextStatuses()
important notice
Because Yii2 ActiveRecord implementation does not fire any event when the method refresh()
is called, the attached SimpleWorkflowBehavior
has no
way to know that the status attribute may have change and that the actual model status maintained internally by the behavior, needs to be updated.
A feature request has been created but until it gets accepted and implemented you must
call initStatus()
after each call to refresh()
. For example :
$post->refresh();
$post->initStatus();
Note that calling initStatus()
is usually never done by the developer but by the behavior itself when specific ActiveRecords
event are fired.
- remove custom validation error message in
WorkflowValidator->init()
(not needed) - when a model changes status, all
WorkflowEvent
events fired now include the start status, the end status and the transitions. This change has been applied to all Event sequences inraoul2000\workflow\events
.
- fix GraphmlLoader to not only process ShapeNode elements, and use node label in yEd as Status Id.
- add method
WorkflowInterface->getAllStatuses()
allowing to get a list of all statuses belonging to a workflow, using the workflow instance.
You can now write :
// assuming our $post has a status let's get a list of all statuses in the current workflow
$allStatuses = $post->getWorkflow()->getAllStatuses();
This feature extends the one implemented in version 0.0.11
- add method
StatusInterface->isInitialStatus()
to test if a status instance is the initial status of the workflow it belongs to. - add argument
$includeCurrent
inWorkflowHelper::getNextStatusListData(...)
. When TRUE the current model status is added to the returned array. When FALSE (default) only next statuses are included. - rollback previous change :
$emptyStringAsNull
is removed. The behavior now applies empty() to detect unset status
- add the configuration setting
$emptyStringAsNull
to SimpleWorkflowBehavior.
When TRUE, the status attribute is considered as null if it contains an empty string If set to FALSE this is developer responsability to nullify the status attribute value, because an empty string is considered as an invalid status id.
- add default Event
The Default event is not related to the configured event sequence, it's a built-in event, fired by the SimpleWorkflowBehavior, in its before and after form, whenever a model changes status. Like any other event, it is an instance of the WorkflowEvent with a name set to :
- SimpleWorkflowBehavior::EVENT_BEFORE_CHANGE_STATUS
- SimpleWorkflowBehavior::EVENT_AFTER_CHANGE_STATUS
Depending on the value returned by getStartStatus(), getEndStatus() it is possible to identify the kind of transition that is being performed by the model :
- getStartStatus() == null : the model is entering into the workflow through the initial status ( returned by getEndStatus() )
- getEndStatus() == null : the model is leaving the workflow from status returned by getStartStatus().
In both cases, a call to getTransition() returns NULL : entering or leaving a workflow is not considered as a transition.
- improve leave workflow management
The action to delete the owner model is now considered as leaving the workflow : the leave workflow event
sequence is fired. Previously, the only way for a model to leave a workflow was by assigning NULL to the status attribute and saving
the model (or by calling sendToStatus(null)
);
- the SimpleWorkflowBehavior can now be safely attached to any object that inherits from \yii\base\Object.
warning : The SimpleWorkflowBehavior has been first designed to be attached to an ActiveRecord
instance and thus integrates in the life cycle
of such objects. By installing event handlers on various ActiveRecord
events, it automatically handles status persistence. If the behavior
is attached to another type of object, the developer must understand and (possibly) implement all the features that otherwise would be available by default.
- add status conversion map setter to the class
raoul2000\workflow\base\StatusIdConverter
. The map is still required by the constructor but it can be updated at runtime using thesetMap()
method. (see dynamic maps for status conversion issue) - Both status converter and status accessor components can now be configured as an object instance.
For example, assuming that variable $myConverter
contains a reference to an object instance
that implements raoul2000\workflow\base\StatusIdConverter
, you can now write :
class Post extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
[
'class' => \raoul2000\workflow\base\SimpleWorkflowBehavior::className(),
'statusConverter' => $myConverter
]
];
}
}
In the previous version it was only possible to initialize the statusConverter
parameter to a string, representing the id of
a component registered in Yii::$app
.
This also applies to statusAccessor
parameter.
- lazy component initialization for status converter and status accessor. If configured, these component are actually initialized (assigned) and validated when accessed for the first time.
- add cache to
WorkflowFileSource
component. If set, thedefinitionCache
parameter defines the cache object to use.
To initialize the WorkflowFileSource
component to use a cache :
$config = [
'components' => [
'workflowSource' => [
'class' => 'raoul2000\workflow\source\file\WorkflowFileSource',
'definitionCache' => [
'class' => 'yii\caching\FileCache',
],
],
- update unit tests
- check interface implemented instead of class
- remove StatusInterface.addTransition() method
- add workflow source component to WorkflowBaseObject constructor. Update Status and WorkflowInterface to enable accessing workflow items from Status or Workflow instances.
For example it is now possible to do the following :
// let's get a status instance from our Post model
$status = $post->getWorkflowStatus();
// get an array containing out going Transitions objects
$status->getTransitions();
// get the parent workflow
$status->getWorkflow();
// get the initial status of the parent workflow
$status->getWorkflow()->getInitialStatus();
Massive refactoring of the workflow source component architecture to allow loading workflow definition from virtually any file (and not only PHP class).
WARNING : this modification may break back compatibility so pay attention to the following major changes :
- namespace
raoul2000\workflow\source\php
has been renamedraoul2000\workflow\source\file
- class
WorkflowPhpSource
has been renamedWorkflowFileSource
- the
namespace
configuration setting has been removed from the source component - add configuration attribute
definitionLoader
toWorkflowFileSource
IWorkflowDefinitionProvider
has been moved to namespaceraoul2000\workflow\source\file
The WorkflowFileSource
component is dedicated to load workflow definition from any file, for this reason,
it relies on a WorkflowDefinitionLoader component that is used to :
- locate the file containing the workflow definition
- load the workflow definition
- convert it into a PHP array having the structure expected by the
WorkflowFileSource
component.
By Default, the WorkflowFileSource component uses a PhpClassLoader
instance, maintaining this way the default feature that
allows a workflow definition to be retrieved from a PHP class. The default namespace remains app\models
and if you want to change
it, you must explicitely declare the Workflow source component with appropriate settings (like required in the previous versions).
In the example below, we declare a Workflow Source component, using the PhpClassLoader
and with a customized namespace attribute
(in our example, workflow definition classes are located in app\models\workflows
).
$config = [
// ....
'components' => [
'workflowSource' => [
'class' => '\raoul2000\workflow\source\file\WorkflowFileSource',
'definitionLoader' => [
'class' => 'raoul2000\workflow\source\file\PhpClassLoader',
'namespace' => 'app\models\workflows'
]
// ...
- add propagateErrorsToModel configuration setting to the SimpleWorkflowBehavior
If TRUE, all errors that may be registred on an invalidated 'before' event, are assigned to the status attribute of the owner model (allowing to display them to the user).
Example :
/**
* This is the model class for table "Post".
*
* @property integer $id
* @property string $name
* @property string $status
*/
class Post extends \yii\db\ActiveRecord
{
public function init()
{
$this->on(
WorkflowEvent::beforeEnterStatus('Post/to-publish'),
function ($event) {
// test if the model can enter in status 'publish'
// ...
if( $error ) {
$event->invalidate('the post can\'t be published');
}
}
);
}
public function behaviors()
{
return [
[
'class' => \raoul2000\workflow\base\SimpleWorkflowBehavior::className(),
'propagateErrorsToModel' => true
]
];
}
}
$post = Post::findOne(1);
$post->status = 'Post/to-publish';
if( $post->save() == false ) {
echo 'error : '.$item->getFirstError('status'); // the post can\'t be published
}
- add stopOnFirstInvalidEvent configuration setting to the SimpleWorkflowBehavior
if TRUE, all "before" events are fired event if one of them is invalidated by an attached handler. When FALSE, the first invalidated event interrupts the event sequence.
- WARNING : relocate WorkflowHelper, now in namespace
raoul2000\workflow\helpers
- add helper function getAllStatusListData()
getAllStatusListData() returns an associative array containing all statuses that belong to a workflow. The array returned is suitable to be used as list data value in (for instance) a dropdown list control.
Usage example : assuming model Post has a SimpleWorkflowBehavior the following code displays a dropdown list containing all statuses defined in $post current the workflow :
echo Html::dropDownList(
'status',
null,
WorkflowHelper::getAllStatusListData(
$post->getWorkflow()->getId(),
$post->getWorkflowSource()
)
)
- update doc
- rename
SimpleWorkflowBehavior::_createTransitionItems
toSimpleWorkflowBehavior::createTransitionItems
- add autoInsert feature.
this feature is not enabled
The autoInsert feature allows to automatically insert a model into a workflow when the model is created and only if there is no previous status set.
Example :
class Post extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
[
'class' => \raoul2000\workflow\base\SimpleWorkflowBehavior::className(),
'autoInsert' => true,
'defaultWorkflowId' => 'MyWorkflow'
]
];
}
}
$post = new Post();
echo $post->getWorkflowStatus()->getId(); // output : MyWorkflow/new
Note that no event is fired when a model is auto-inserted into a workflow. If autoInsert is a string, it must be the ID of the workflow where the model will be automatically inserted to. If autoInsert is a TRUE boolean, the model is inserted into its default workflow.
- add support for multi workflow : more than one workflow can be attached to a model.
The first declared SimpleWorkflow behavior handles the main workflow and all other SimpleWorkflow behavior handle secondary workflows. All SimpleWorkflow behavior related to secondary workflow must provide configuration settings for :
- statusAttribute
- defaultWorkflowId
Moreover, SimpleWorkflow behavior related to secondary workflows must NOT be declared as anonymous : a behavior name is required.
Example : Item08Workflow1 is the primary workflow, Item08Workflow2 is the secondary workflow
class Post extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
// The main workflow is ALWAYS declared first
[
'class' => \raoul2000\workflow\base\SimpleWorkflowBehavior::className(),
],
// the secondary workflow : note that it is declared as 'w2'
'w1' => [
'class' => \raoul2000\workflow\base\SimpleWorkflowBehavior::className(),
'statusAttribute' => 'status_ex',
'defaultWorkflowId' => 'SecondaryWorkflow'
]
];
}
}
To access SimpleWorkflow methods related to the main workflow, you can use the usual way. To access SimpleWorkflow methods related to the secondary workflow, you must use the behavior name.
In both case you can also use direct attribute assignement.
Example
$p = new Post();
// direct attribute assignement
$p->status = 'PostWorkflow/draft';
$p->status_ex = 'SecondaryWorkflow/ready';
// both transitions are committed now
$p->save();
// SimpleWorkflowBehavior methods
$o = new Post();
// applied on the main workflow only
$o->enterWorkflow();
// applied on the secondary workflow
$p->getBehavior('w1')->enterWorkflow();
See unit test tests\unit\workflow\behavior\MultiWorkflowTest
for more example.
- add MinimalArrayParser for workflow definition PHP arrays provided as for instance :
[
'draft' => ['ready', 'delivered'],
'ready' => ['draft', 'delivered'],
'delivered' => ['payed', 'archived'],
'payed' => ['archived'],
'archived' => []
]
The initialStatusId is the first status defined (here draft)
- add tests for raoul2000\workflow\source\php\DefaultArrayParser
- minor fix in DefaultArrayParser
- externalize array parser to normalize workflow php array definition
- change regex for status and workflow ID pattern: now
/^[a-zA-Z]+[[:alnum:]-]*$/
- Improve WorkflowPhpSource parsing of a workflow defined as a PHP array.
[
'initialStatusId' => 'A',
'status' => [
'A' => [
'transition' => 'A, B'
],
'B' => [
'transition' => ['A','C']
],
'C'
]
]
- Initial import