Skip to content

Commit

Permalink
add cache for workflow definition (WorkflowFileSource)
Browse files Browse the repository at this point in the history
  • Loading branch information
raoul2000 committed Jun 6, 2015
1 parent b6ab42a commit 139aa4a
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 135 deletions.
17 changes: 15 additions & 2 deletions CHANGE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#version 0.0.12
- add status conversion map setter to the class `raoul2000\workflow\base\StatusIdConverter`. The map is still required by the constructor
- **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 the `setMap()` method. (see [dynamic maps for status conversion issue](https://github.com/raoul2000/yii2-workflow/issues/9))
- Both status converter and status accessor components can now be configured as an object instance.

Expand All @@ -25,9 +25,22 @@ 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)
- **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, the `definitionCache` parameter defines the cache object to use.

To initialize the `WorkflowFileSource` component to use a cache :

```php
$config = [
'components' => [
'workflowSource' => [
'class' => 'raoul2000\workflow\source\file\WorkflowFileSource',
'definitionCache' => [
'class' => 'yii\caching\FileCache',
],
],
```
#version 0.0.11
- update unit tests
- check interface implemented instead of class
Expand Down
260 changes: 137 additions & 123 deletions guide/source-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,125 +7,26 @@ To be able to handle various file formats, the *WorkflowFileSource* component re
locating and loading a file is delegated to a class implementing the `WorkflowDefinitionLoader` interface. There are currently 3 types
of workflow definition loader available with yii2-workflow:

- `PhpClassLoader` : loads the workflow definition from a class that implemented the `IWorkflowDefinitionProvider` interface. This is
- `PhpClassLoader` : loads the workflow definition from a class that implements the `IWorkflowDefinitionProvider` interface. This is
**the default loader** used by the file source component.
- `PhpArrayLoader` : loads the workflow definition from a PHP file that must returns an array representing the workflow definition.
- `PhpArrayLoader` : loads the workflow definition from a PHP file that must returns a PHP array representing the workflow definition.
- `GraphmlLoader` : loads the workflow definition from a Graphml file.

The two first loader are somewhat equivalent in the way they expect to read workflow definition: they both expected to get a PHP array.
On the other side, the GraphmlLoader expects to read an XML file.

## PHP class Loader
## Workflow definition as PHP Array
Both `PhpClassLoader` (default loader) and `PhpArrayLoader` expect to read workflow definition as PHP array. In this chapter we are going
to describe the structure of this array.

Loads the workflow definition from a PHP class.
### Array Structure

### Namespace : workflow location

By default the `PhpClassLoader`component loads workflows from the `app\models` namespace.
So for example in the following delcaration, the default workflow associated with the *Post* model will be loaded from
the class `app\models\MyWorkflow` :

```php
namespace app\models;
class Post extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
'class' => '\raoul2000\workflow\base\SimpleWorkflowBehavior',
'defaultWorkflowId' => 'MyWorkflow'
];
}
}
```

If you need to change the default namespace value you have two options : the fast one and the not so fast one.

#### The magic alias

As the *PhpClassLoader* is the default loader used with the default source component, it is a common task to change the namespace value
used to load PHP classes. Consequently having to explicitely declare component just to change one configuration setting is too much work (and
we know good developpers are lazy). For this purpose the alias `@workflowDefinitionNamespace` is available to define globally the namespace value.

For instance, in you `index.php` file, declare this alias :

```php
Yii::setAlias('@workflowDefinitionNamespace','app\\models\\workflows');
```

By doing so, all workflow definition classes will be loaded from the `app\models\workflows` namespace. Note that this alias overrides any specific
namespace configuration that you may have defined the *standard ways*.


#### Standard

In general if you need to change any configuration setting, you must explicitely declare it as an Yii2 application component and no rely
on *SimpleworkflowBehavior* to do it for you.
In the example below, we are defining the source component using the default Id (*workflowSource*)
and set the namespace used by `PhpClassLoader` to the location where workflow definitions are supposed to be located (here *@app/models/workflows*).

```php
$config = [
'components' => [
'workflowSource' => [
'class' => 'raoul2000\workflow\source\file\WorkflowFileSource',
'definitionLoader' => [
'class' => 'raoul2000\workflow\source\file\PhpClassLoader',
'namespace' => '@app/models/workflows'
]
],
```

As you may have guessed, there is only one namespace per workflow source component so you are encouraged to locate all your workflows in the
same folder. In the case you must load workflows from various location, you should declare another workflow source component (one per namespace) but
remember that each workflow source component serves workflows from only one namespace (folder).


## PHP array Loader

Loads workflow definition for a PHP array stored in a file.

First configure the workflow file source component to use the `PHPArrayLoader` class. In this example, workflow definition files are assumed
to be located in `@app/models/workflows`.

```php
$config = [
'components' => [
'workflowSource' => [
'class' => 'raoul2000\workflow\source\file\WorkflowFileSource',
'definitionLoader' => [
'class' => 'raoul2000\workflow\source\file\PhpArrayLoader',
'path' => '@app/models/workflows'
]
],
```

Now if we want to create the definition for the workflow *post*, we just create the file *post.php* in the folder `@app/models/workflows`.

```php
return [
'initialStatusId' => 'draft',
'status' => [
'draft' => [
'transition' => ['publish','deleted'
]
],
'publish' => [
'transition' => ['draft','deleted']
],
'deleted' => [
'transition' => ['draft']
]
]
];
```

### The Workflow
#### The Workflow

The PHP array defining a workflow is an associative array that must contains 2 keys : **initialStatusId** and **status**.

- *initialStatusId* : `string` that represents the ID of the initial status
- *status* : `array` associative array defining each status that belong to the workflow.
- *status* : `array` associative array defining each status that belonging to the workflow.

```php
[
Expand All @@ -136,7 +37,7 @@ The PHP array defining a workflow is an associative array that must contains 2 k
]
```

### Status List Definition
#### Status List Definition

The status list definition is an associative array where keys are status Ids and values are status definitions.
If a status doesn't need any particular definition, it can be defined directly as a string value.
Expand All @@ -154,11 +55,11 @@ In the example below, both *draft* and *pusblised* have a specific definition, b
]
```

### Single Status Definition
#### Single Status Definition

A Single Status Definition is an associative array that may contains 2 specific keys : **transition** and **label**

- *transition* : `array|string` list of ids for all statuses that can be reached
- *transition* : `array|string` list of ids for all statuses that can be reached.
- *label* : `string` user friendly name. If not set, the label is automatically created from the status Id.

```php
Expand All @@ -173,8 +74,7 @@ A Single Status Definition is an associative array that may contains 2 specific
]
```


### Transition Definition
#### Transition Definition

A Transition Definition is an array or a string defining the list of status that can be reached from the current status.
In the example below, we are defining a workflow with following transitions:
Expand Down Expand Up @@ -211,10 +111,10 @@ could also be written this way :
]
```

### Metadata
#### Metadata

Ok, we are now able to create workflows ! We can define statuses and transitions between them. As you you can see, the minimum attributes for a status is
its *id* and optionally we can set a *label*, but that's all. Well, that's not a lot. What if I need to add more properties to my statuses ? Like for instance
its *id* and optionally we can set a *label*, but that's all. Well, that's not a lot. What if we need to add more properties to our statuses ? Like for instance
it could be nice to associate a color with each status, and display this color to the user (users like colors). The solution is *metadata*.

The *metadata* allows you to add almost any attribute not only to statuses, but also to workflow and transition. Let's see that on an example where we are
Expand All @@ -231,7 +131,7 @@ going to add a *color* and an *icon* metadata to the *published* status.

Later on we will be able to retrieve these value of course, and use them the way we want (for instance with a nice and colorful display).

### Example
#### Example

As an example we will use our Post workflow desinged earlier to manage our publishing plateform web app.

Expand Down Expand Up @@ -285,10 +185,114 @@ class PostWorkflow implements raoul2000\workflow\base\IWorkflowDefinitionProvide
}
```

### Loader components

#### PHP class Loader

*Loading workflow definitions from a PHP class.*

##### Namespace : workflow location

By default the `PhpClassLoader`component loads workflows from the `app\models` namespace.
So for example in the following delcaration, the default workflow associated with the *Post* model will be loaded from
the class `app\models\MyWorkflow` :

```php
namespace app\models;
class Post extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
'class' => '\raoul2000\workflow\base\SimpleWorkflowBehavior',
'defaultWorkflowId' => 'MyWorkflow'
];
}
}
```

If you need to change the default namespace value you have two options : the fast one and the not so fast one.

##### The magic alias

As the *PhpClassLoader* is the default loader used with the default source component, it is a common task to change the namespace value
used to load PHP classes. Consequently having to explicitely declare component just to change one configuration setting is too much work (and
we know good developpers are lazy). For this purpose the alias `@workflowDefinitionNamespace` is available to define globally the namespace value.

For instance, in you `index.php` file, declare this alias :

```php
Yii::setAlias('@workflowDefinitionNamespace','app\\models\\workflows');
```

By doing so, **all workflow definition classes** will be loaded from the `app\models\workflows` namespace. Note that this alias overrides any specific
namespace configuration that you may have defined the *standard ways*.

## graphml document Loader

Loads workflow definition from a *graphml* file.
##### Standard

In general if you need to change any configuration setting, you must explicitely declare it as a Yii2 application component and not rely
on *SimpleworkflowBehavior* to do it for you.
In the example below, we are defining the source component using the default Id (*workflowSource*)
and set the namespace used by `PhpClassLoader` to the location where workflow definitions are supposed to be located (here *@app/models/workflows*).

```php
$config = [
'components' => [
'workflowSource' => [
'class' => 'raoul2000\workflow\source\file\WorkflowFileSource',
'definitionLoader' => [
'class' => 'raoul2000\workflow\source\file\PhpClassLoader',
'namespace' => '@app/models/workflows'
]
],
```

As you may have guessed, there is only one namespace per workflow source component so you are encouraged to locate all your workflows in the
same namespace. In the case you must load workflows from various location, you should declare another workflow source component (one per namespace) but
remember that each workflow source component serves workflows from only one namespace (folder).

#### PHP array Loader

**Loading workflow definitions for a PHP array stored in a file.**

First configure the workflow file source component to use the `PHPArrayLoader` class. In this example, workflow definition files are assumed
to be located in `@app/models/workflows`.

```php
$config = [
'components' => [
'workflowSource' => [
'class' => 'raoul2000\workflow\source\file\WorkflowFileSource',
'definitionLoader' => [
'class' => 'raoul2000\workflow\source\file\PhpArrayLoader',
'path' => '@app/models/workflows'
]
],
```

Now if we want to create the definition for the workflow *post*, we just create the file *post.php* in the folder `@app/models/workflows`.

```php
return [
'initialStatusId' => 'draft',
'status' => [
'draft' => [
'transition' => ['publish','deleted'
]
],
'publish' => [
'transition' => ['draft','deleted']
],
'deleted' => [
'transition' => ['draft']
]
]
];
```
## Workflow definition as Graphml file

*Loading workflow definition from *graphml* files.*

From the [The GraphML File Format](http://graphml.graphdrawing.org/) web site :

Expand Down Expand Up @@ -319,7 +323,7 @@ $config = [
> yEd is a powerful desktop application that can be used to quickly and effectively generate high-quality diagrams.
With this (free) application you can create a workflow and save it as a *graphml* file that can be used as a source for *SimpleWorkflow*.
This is intresting in particular if you have to deal with big workflows made of more than 10 status, with plenty of transitions that make it
This is interesting in particular if you have to deal with big workflows made of more than 10 status, with plenty of transitions that make it
look like a plate of spaghetti.

The only tricky thing is that you must define the *custom* property initialStatusId that is required
Expand All @@ -341,11 +345,21 @@ with ok.
Your workflow is now ready to be used as a workflow source by *SimpleWorkflow*


## Cache

The `WorkflowFileSource` is able to use a cache component to optimize the workflow definition loading task, that can be significant, in particular with
workflows containing a lot of status. Another opportunity to use a cache component is if the workflow definition is provided as a Graphml file. In this such a case,
if no cache is used, the `WorkflowFileSource` component needs to read and parse the Graphml file quite often (at least once per request).

To configure a cache component you must use the `definitionCache` parameter. For example :






```php
$config = [
'components' => [
'workflowSource' => [
'class' => 'raoul2000\workflow\source\file\WorkflowFileSource',
'definitionCache' => [
'class' => 'yii\caching\FileCache',
],
],
```
7 changes: 4 additions & 3 deletions src/source/file/GraphmlLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class GraphmlLoader extends WorkflowDefinitionLoader
* @var string path where the graphml file to load is located
*/
public $path = '@app/models';

/**
* Maps a named attribute with a id attribute defined in the input graphml file.
*
Expand Down Expand Up @@ -47,8 +46,10 @@ class GraphmlLoader extends WorkflowDefinitionLoader
private $_yedProperties = [];

/**
*
* @param unknown $workflowId
* Loads the definition of the workflow whose id is passed as argument.
*
* @param string $workflowId
* @param IWorkflowSource the workflow source component
* @throws WorkflowException
* @return array the workflow definition
*/
Expand Down
Loading

0 comments on commit 139aa4a

Please sign in to comment.