Closed feor58 closed 8 years ago
Hi,
What you should do is to implement the IWorkflowSource
interface and extend yii\base\Object
, You can use WorkflowFileSource
as an example. Then next step is to create and return the three basic objects types :
raoul2000\workflow\base\Status
raoul2000\workflow\base\Transition
raoul2000\workflow\base\Workflow
You can also create your own basic classes for these 3 ones by simply implementing StatusInterface
, TransitionInterface
and WorkflowInterface
.
If you have other questions, feel free to ask ...
ciao
Hi, if you have a db source up an running i would appreciate it if you share?!
Hi Raoul, Thanks for your quick answer. I see the next schema: class WorkflowSource (new class) implements iWorkflowSource class WorkflowFileSource extends WorkflowSource class WorkflowDbSource (this is my target class) extends WorkflowSource What is your opion from this? Thanks.
WorkflowSource an abstract class, of course.
Yes, it seems ok ... however, I don't understand why you need this WorkflowSource
abstract class and why not create WorkflowDbSource
to implement the IWorkflowSource
interfrace ?
use yii\base\Object;
use raoul2000\workflow\source\IWorkflowSource;
use raoul2000\workflow\base\Status;
use raoul2000\workflow\base\Transition;
use raoul2000\workflow\base\Workflow;
use raoul2000\workflow\base\WorkflowException;
class WorkflowDbSource extends Object implements IWorkflowSource
{
// your DB implementation here ....
}
Yes, I understand this, however, i sew my WorkflowDbSource class have nearly same methods as the WorkflowFileSource class. That's why i thought the abstract WorkflowSource class. At the same time WorkflowFileSource and WorkflowDbSource are logically same level for me, so i didn't want to extend the WorkflowFileSource class.
i sew my WorkflowDbSource class have nearly same methods as the WorkflowFileSource class.
That is correct, they should both implementing the IWorkflowSource
interface.
At the same time WorkflowFileSource and WorkflowDbSource are logically same level for me
That's correct again, they are at the same level, they both implement the IWorkflowSource
interface, and that's why In my opinion there is no need for an intermediate absrtact class, but that's up to you ....
I see WorkflowFileSource class have further methods beyond IWorkflowSource methods which are nearly same as in my planned WorkflowDbSource class. These are: getDefinitionLoader() ??? getDefinitionCache() getClassMap() getClassMapByType() parseStatusId() isValidStatusId() isValidWorkflowId() isValidStatusLocalId() addWorkflowDefinition() validateWorkflowDefinition() I think to put these methods into WorkflowSource class. Ultimately, in my WorkflowDbSource.php can be class WorkflowDbSource extends WorkflowSource implements IWorkflowSource {} Of course the following is possible, too class WorkflowDbSource extends WorkflowFileSource, however because my opion WorkflowFileSource and WorkflowDbSource are same level so the former is more friendly for me. What think you?
I suggest the following file structure:
.../src/source/ IWorkflowDefinitionProvider.php IWorkflowSource.php WorkflowArrayParser.php WorkflowDefinitionLoader.php WorkflowSource.php
.../src/source/file/ GraphmlLoader.php PhpArrayLoader.php PhpClassLoader.php WorkflowFileSource.php
.../src/source/db/ DbLoader.php WorkflowDbSource.php
ok I understand now .
However, in your proposed structure I don't think that IWorkflowDefinitionProvider
, WorkflowArrayParser
and WorkflowDefinitionLoader
should be moved to src/source. These classes and interfaces are very specific to the WorkflowFileSource
and related components. They are responsible for loading file (plain php, php class or gaphml files) and parse the workflow definition into a format expected by WorkflowFileSource
. For a DB source, they are not needed ...
Again, this is my opinion but if you are more comfortable with this structure, go with it !!
Thanks for the useful info. I thought forward. I am able to get the same definition array from database as that PhpArrayLoader provide. I think from here my way would be same (parsing, getting obejcts - Workflow, Status, Transition) as in case of WorkflowFileSource. I find the class structure for this. I wouldn't like to copy the full WorkflowFileSource class into WorkflowDbSource. Help me, please.
do you mean that you want to store in DB, the workflow definition as PHP array ? ... you want to replace file storage with DB storage but keep the same structure ?
My base tables are:
CREATE TABLE IF NOT EXISTS w_workflows
(
id
bigint(20) unsigned NOT NULL AUTO_INCREMENT,
name
varchar(50) NOT NULL,
status
varchar(50) NOT NULL,
is_systemtype
tinyint(1) NOT NULL DEFAULT '1',
initial_node
varchar(50) NOT NULL,
max_open_instances
tinyint(3) DEFAULT NULL,
ngsf_tid
bigint(20) unsigned DEFAULT NULL,
ngsf_arid
bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (id
),
UNIQUE KEY uk
(ngsf_tid
,ngsf_arid
,name
),
KEY ngsf_arid
(ngsf_arid
),
KEY ngsf_tid
(ngsf_tid
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='{"alias":"w"}' AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS w_nodes
(
id
bigint(20) unsigned NOT NULL AUTO_INCREMENT,
wid
bigint(20) unsigned NOT NULL,
name
varchar(50) NOT NULL,
label
varchar(40) DEFAULT NULL,
PRIMARY KEY (id
),
UNIQUE KEY uk
(wid
,name
),
KEY wid
(wid
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='{"alias":"wn"}' AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS w_edges
(
id
bigint(20) unsigned NOT NULL AUTO_INCREMENT,
wnid_from
bigint(20) unsigned DEFAULT NULL,
wnid_to
bigint(20) unsigned DEFAULT NULL,
label
varchar(40) DEFAULT NULL,
menu_url
varchar(128) DEFAULT NULL,
menu_linkoptions
varchar(128) DEFAULT NULL,
PRIMARY KEY (id
),
UNIQUE KEY uk
(wnid_from
,wnid_to
),
KEY wtnid_from
(wnid_from
),
KEY wnid_to
(wnid_to
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='{"alias":"we"}' AUTO_INCREMENT=1 ;
Yes, from these tables i can to build the same php definition array as that the PhpArrayLoader produce (this is the DbLoader class).
I guesses these table are designed to meet your specific needs as there are plenty of columns which are not required by yii2-workflow. I think that creating a PHP array from table rows (with your DbLoader) and pass it to the workflow source is not the simplest way to go, but it can work fine.
i don't understand what would be even simpler?
ok, I have created an example of something simple to implement WorkflowDbSource
. It took me less than 2 hours so you can ilmagine it is not ready to be used in production, however it's a good prototype of how I would implement a WorkflowDbSource
component.
I ran simple tests with no problem.
The DB schema is basic :
You can find the source code in Gist :
The main component is the WorkflowDbsource
It is meant to be a generic implementation and it misses many features that a production-ready component should include (optimisation and verifications), but as you can see, I just had to implement the IWorkflowSource
interface to be able to use DB as a source (I've used 3 models generated automatically by Gii to avoid writting SQL queries .. yes I'm lazy sometime).
I hope it will clarify what I mean by simpler, but remember that I don't know anything about your specific requirements and so I don't have the same problem to solve thay you may have with your (future) app.
:sunglasses:
ps: I have also created a UML schema so you can see how yii2-workflow classes architecture. Hope it helps ...
Yes, it helps. This is a very cool comment for a very cool project. Thanks.
Hi, as I would need this for a project, did you enhance that dbsource? I would be interested in how you manage the ui in detail? ;)
Hey, thanks for reopening! Have a pretty raw version of the management because of your sample code... actually if you can support this - i would at least invite you to a wine!
Hi, as I don't currently need such component I didn't enhaced it up to now (and I don't know if @feor58 did something about it).
Regarding the UI, same thing .... I played a little with visjs on yii2-workflow-view but nothing related with the DB up to now.
Of course I'm ready to help/support (and not only because of the wine :smirk: ) so fell free to ask...
Thanks a lot - i just put the latest version of the module online - open - would be great if @feor58 has something done already! And thanks for the workflow view! it's awesome and great for "sales" ;)
Repo:
So, after setting all up, I tried to include the workflow like this:
public function behaviors()
{
return [
TimestampBehavior::className(),
'softDeleteBehavior' => [
'class' => SoftDeleteBehavior::className(),
'softDeleteAttributeValues' => [
'deleted_at' => time()
],
'replaceRegularDelete' => true,
],
'customerWorkflow' => [
'class' => \raoul2000\workflow\base\SimpleWorkflowBehavior::className(),
'source' => \app\modules\workflow\components\WorkflowDbSource::className(),
]
];
}
But it fails with message:
How do I need to "assign the workflow"? THANKS!
Btw: management looks like this:
The parameter source must contain the name of an application component (the workflow source) to use with the behavior, and not (like you wrote) the class name. So what you should do is :
$config = [
// ....
'components' => [
// declare your source component and give it a name
'myDbSource' => [
'class' => \app\modules\workflow\components\WorkflowDbSource::className()
]
// ...
namespace app\models;
class MyModelextends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
[
'class' => \raoul2000\workflow\base\SimpleWorkflowBehavior::className(),
'source' => 'myDbSource'
]
];
}
}
For a personal project I render workflow status using the bootstrap label component (the column Etat) .. I felt it thats it brings a little bit of color and readability.
Then of course, one solution would be to CRUD the workflow through a 100% graphicla way .... with visjs for instance (but I think we already agreed that it would require quite a lot of work).
Thanks, that's awesome but how do you seperate several workflows stored within one table... e.g. CustomerWorkflow, UnitWorkflow, ProspectingWorkflow, etc...
btw. are you managing Tennis Competitions?
well in my case that's true, all items in the grid belong to the same workflow .... If that's not the case, I'm afraid it is required to also display the workflow name. Maybe but 2 labels side by side (one for the workflow name, one for the status) ? ... humm I'm not sure (I have poor designer skills).
The webapp I'm working on is dedicated to manage any type of competition so it will adapt to any type of organisation (layout) : tournament, division, etc ... well, at least that's the goal. I'm making heavy uses of yii2-workflow and up to now it I'm happy with it :smile:
great app - and for the workflow, i think about passing over the "workflow id" to the dbsource so you can filter down to one of the models...?!
Maybe you don't have to do that but just play with the gridview and add 2 computed columns : one for the workflow ID (e.g. CustomerWF) and another one for the status Id (e.g. Active).
Something like this :
GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
'id',
'name',
[
'attribute' => 'workflow_id',
'format' => 'html',
'value' => function($model, $key, $index, $column) {
return substr($model->status,0,strpos($model->status,'/'));
}
],
[
'attribute' => 'status_id',
'format' => 'html',
'value' => function($model, $key, $index, $column) {
return substr($model->status,strpos($model->status,'/')+1);
}
],
'status',
['class' => 'yii\grid\ActionColumn'],
],
]);
By modifying the ModelSearch class, you should be able to implement filtering these 2 calculated columns. For the sorting I'm not sure .. maybe sorting on a substring ?
That's just an idea and of course it requires a little bit of work to be validated as an acceptable solution ....
great concept - will add it too - any idea about the passing wfID to the dbsource? thanks for your help
On Oct 6, 2015, at 10:44 PM, raoul notifications@github.com wrote:
Maybe you don't have to do that but just play with the gridview and add 2 computed columns : one for the workflow ID (e.g. CustomerWF) and another one for the status Id (e.g. Active).
Something like this :
GridView::widget([ 'dataProvider' => $dataProvider, 'filterModel' => $searchModel, 'columns' => [ 'id', 'name', [ 'attribute' => 'workflow_id', 'format' => 'html', 'value' => function($model, $key, $index, $column) { return substr($model->status,0,strpos($model->status,'/')); } ], [ 'attribute' => 'status_id', 'format' => 'html', 'value' => function($model, $key, $index, $column) { return substr($model->status,strpos($model->status,'/')+1); } ], 'status', ['class' => 'yii\grid\ActionColumn'], ], ]);
By modifying the ModelSearch class, you should be able to implement filtering these 2 calculated columns. For the sorting I'm not sure .. maybe sorting on a substring ?
That's just an idea and of course it requires a little bit of work to be validated as an acceptable solution ....
— Reply to this email directly or view it on GitHub https://github.com/raoul2000/yii2-workflow/issues/13#issuecomment-145995380.
Sorry I thought your question was related to display multi workflow statuses in the grid...
I don't understand why you would need to passe a workflow Id to the dbSource component... To which method do you want to pass it ? to the constructor ? Is it related to your question ("how do you seperate several workflows stored within one table ?")
Well maybe I don't get it, how can i only lookup transitions for the customer workflow if I have a unit workflow stored in the same tables? I don't wanna have a set of three tables for each workflow I might use;)
Maybe that's me who don't get it :smirk: (my english skills are ... average)
A status name is made of a workflow id and a status id separated with a slash char (example : customer/active). In the three tables (_swworkflow, _swstatus, _swtransitions) you'll always find a column for workflow id, and one for status id. So for instance if you want to get all transitions for workflow customer you can write someting like :
$transitions = SwTransition::findAll([
'start_status_workflow_id' => 'customer',
'end_status_workflow_id' => 'customer'
]);
(Actually the example above is not appropriate if you have cross workflow transitions)
With the three tables (_swworkflow, _swstatus, _swtransitions) it should be possible to store many workflows/status/transitions. However, note that this DB schema is just one solution among others (maybe better ones).
ah, so i guess it’s my fault! the “id” is the “key” ;) sorry I’m not a good coder, so I should have had the chance to get it from the code… Thanks for your support!
On Oct 7, 2015, at 10:44 PM, raoul notifications@github.com wrote:
Maybe that's me who don't get it (my english skills are ... average)
A status name is made of a workflow id and a status id separated with a slash char (example : customer/active). In the three tables (sw_workflow, sw_status, sw_transitions) you'll always find a column for workflow id, and one for status id. So for instance if you want to get all transitions for workflow customer you can write someting like :
$transitions = SwTransition::findAll([ 'start_status_workflow_id' => 'customer', 'end_status_workflow_id' => 'customer' ]); (Actually the example above is not appropriate if you have cross workflow transitions)
With the three tables (sw_workflow, sw_status, sw_transitions) it should be possible to store many workflows/status/transitions. However, note that this DB schema is just one solution among others (maybe better ones).
— Reply to this email directly or view it on GitHub https://github.com/raoul2000/yii2-workflow/issues/13#issuecomment-146322181.
no problem ... :smile:
thanks for support again! ;)
Hi! I would like a WorkflowDbSource class. Which files should extend according to best practice?
Thanks.