How to create a model out of a "polymorphic association"

Hello everyone ,

I am using yii for the first time for my project, and I am stuck at some point…I will describe the problem and hope someone can give me an idea how to solve it. If you need my db structure or code please tell me.

The company offers trainings and each training can be of a specific process (only 2 until now: FB and DSM). So I have a table ‘trainings’ and a table ‘processes’. For each training there are some tasks to be done before and after the training (ex: print material, certificate, get feedback etc…so a list of checkboxes) The problem was that for each process, the tasks were different (mixed)! So I could not just make a table for ‘tasks before training’ and ‘tasks after training’…I read that this was called “Polymorphic Association“…anyway, I did 3 tables were one of them is called “process_constraint” used to connect table ‘processes’ with the 2 tasks tables, so eg. it assigns to process FB some of the attributes of table ‘tasks before training’ and so on…

The problem is this: When a training is created I also want an object of ‘tasks’ to be created, depending on the process the training is based on…but I can’t do it using gii since I don’t have only one table. Plus, I am confused how and where can I store the task list I want to generate (since it will be updated from time to time until all tasks are completed). I hope my explanation is clear enough. I want to know how can I solve it? I don’t even know where to start…

Thank you for your help

You needn’t to use gii to do this.

If you want that when a training is created, also an object of tasks is created, than you can make:





// First create a traning object;

$training = new Training();

....    // code

$training->save();


// Then create a task object;

$task = new Task();

....    // code

$task->save();


// At last, create constraint object;

$pc = new ProcessContraint();

$pc->training_id = $training->id;

$pc->task_id = $task->id;

$pc->save();



You can write these lines into a transaction to be sure that all row will be executed.

Thank you for your answer, but I am not sure if I understood correctly or I did not explain my problem properly.

How can I create “new Task()” if I don’t have a Task Model? Eg. the ‘task before training’ table contains 25 attributes, but for (lets say) process1 there should be a list with only 16 out of those 25 attributes (this is defined in constraint table)? So a list with 16 checkBoxes. And I have to update this list (check the checkbox) if a task is finished…where do I save this list?

I will upload my db structure (only for this part, and I did it in ppt…sorry for that)

You can see from the structure that for a training I have to create a beforeTask list, and for each process, the attributes the list should contain are defined in processConstraint table…6168

db.png

Let’s start from the beginning.

In the simplest case, we have a table BeforeTasks (or AfterTasks, is the same thing) the same for all tasks.

If so, you may link directly to the table BeforeTasks the Process table, setting the table BeforeTasks as:

BeforeTasks (unique for all tasks):

  • idBT;

  • idProcess;

  • name;

  • value;

And so the problem would be solved.

Now let’s say there are 3 models of BeforeTasks, with a number of different fields: BeforeTasks1, BeforeTasks2, BeforeTasks3.

Nothing stopping you from creating these three tables, on the same basis of previous BeforeTasks and record the 3 different types.

Only that if for example you want to know how many there are BeforeTasks for one process, you have to go to interrogate all 3 (generally n) tables, to see if there are records related to the process.

A more general way could be at this point to put a third table.

The table BeforeTasks_Process should be so:

  • idBTP;

  • idProcess: id of Process;

  • idBT: id of specialized BeforeTasks table;

  • typeBT: this field you need to understand then what will be the next table that will specialize the type of BeforeTasks; will have for example the following values: "beforeTask1", "beforeTask2", "beforeTask3".

and no other field.

So, in this more general case, you have to:

  1. Create an object BeforeTask the type that interests you, such BeforeTask2:

$bt = new BeforeTask2();

$bt-> save ();

  1. Create the record BeforeTasks_Process:

$ btp BeforeTasks_Process = new ();

$btp = new BeforeTask_Process();

$btp->idProcess = $process->id;

$btp->idBT = $bt->id;

$btp->typeBT = ‘beforeTask2’;

$btp->save();

So if you want to know how many (regardless of specialization) BeforeTask there are for a given process is sufficient:

SELECT COUNT (*) FROM BeforeTasks_Process WHERE idProcess = %idProcess%

If you want to know how many there are BeforeTask2 for a given process, simply:

SELECT COUNT (*) FROM BeforeTasks_Process WHERE idProcess = %idProcess% AND typeBT = ‘beforeTask2’;

Was this your question?

Hello Fabrizio,

Thank you very much for taking the time to help me.

And sorry if I may sound "stupid", I am a newbie…

In the more general way you mentioned I did not understand the "idBT: id of specialized BeforeTasks table;",

cause I don’t know how this could be a fk if there would be 3 (or more) before_tasks tables…

The first way you said I think is putting me in the right track…And as my main problem is where to store the values every time a "before task" list is created, I was thinking of not connecting before_tasks tables with the process table at all, instead I could create 2 different before_tasks tables each containing the tasks as fields:

Table before_task1

-id

-idTraining

-bt1

-bt2

-bt3

-…

And lets say if I create a new Training which is a Process 1, then I create a new record in before_task1 table (all tasks set to 0). If training is Process 2, I create a record in before_task2 table. And I show this task list to the user (list of checkboxes)which he/she will update from time to time. And if all tasks are done (set to 1) the list is completed…

Would this be ok, right?

In case of idBT you can’t implement fk. At most you can implement triggers at database level to simulate referential integrity.

But if haven’t specific needs, you can implement as many as typology of BeforeTask tables.

Ok Fabrizio, thanks :)