Reputation: 105914
I have a basic M:N setup with three tables: candidate, position, and candidate_position.
Here's a screenshot of the ERD from MySQL Workbench
Now, moving on from that let's talk about forms. In the default world of symfony generator, you'd have a separate CRUD interface for all three of these tables. However, I don't want to have a CRUD interface for candidate_position
.
What I want, is for the create and edit actions of the Candidate interface to contain a multi-choice field (select list, checkbox array, whatever) that would create the CandidatePosition records as part of the Candidate actions.
On to the code
config/doctrine/schema.yml (Note: the is not the entire schema.yml, but only the tables discussed here)
---
detect_relations: true
options:
type: InnoDB
candidate:
columns:
id:
type: integer(4)
primary: true
unsigned: true
notnull: true
autoincrement: true
first_name:
type: string(45)
notnull: true
last_name:
type: string(45)
notnull: true
created_at:
type: integer(4)
unsigned: true
relations:
Positions:
class: Position
refClass: CandidatePosition
local: candidate_id
foreign: position_id
position:
columns:
id:
type: integer(4)
primary: true
unsigned: true
notnull: true
autoincrement: true
name:
type: string(45)
relations:
Candidates:
class: Candidate
refClass: CandidatePosition
local: position_id
foreign: candidate_id
candidatePosition:
tableName: candidate_position
columns:
candidate_id:
type: integer(4)
primary: true
unsigned: true
notnull: true
position_id:
type: integer(4)
primary: true
unsigned: true
notnull: true
indexes:
fk_candidate_position_candidate1:
fields: [candidate_id]
fk_candidate_position_position1:
fields: [position_id]
apps/backend/modules/candidate/config/generator.yml
generator:
class: sfDoctrineGenerator
param:
model_class: Candidate
theme: admin
non_verbose_templates: true
with_show: false
singular: ~
plural: ~
route_prefix: candidate
with_doctrine_route: true
actions_base_class: sfActions
config:
actions: ~
fields:
first_name: { label: First Name }
last_name: { label: Last Name }
created_at: { label: Created On }
candidate_positions: {label: Positions}
list:
sort: [last_name, asc]
filter: ~
form:
display:
"User": [first_name, last_name]
"Applying For": [candidate_positions]
fields :
hide: [created_at]
edit: ~
new: ~
lib/form/doctrine/candidateForm.class.php
class candidateForm extends BasecandidateForm
{
public function configure()
{
unset( $this['created_at'] );
$this->widgetSchema['candidate_positions'] = new sfWidgetFormDoctrineChoice(
array( 'multiple' => true, 'model' => 'Position', 'renderer_class' => 'sfWidgetFormSelectCheckbox' )
);
$this->validatorSchema['candidate_positions'] = new sfValidatorDoctrineChoice(
array( 'multiple' => true, 'model' => 'Position', 'min' => 1 )
);
}
}
This all works, except when it comes to actually saving the data. This is the point where I get stuck.
I clearly need to do something to create/edit/delete the CandidatePosition records, but I'm not sure where to start working. In candidateActions
? Override Basecandidate::save()
?
Let me know if there's any other data you might need to see.
Upvotes: 0
Views: 2208
Reputation: 574
first of all I can suggest an upgrade of symfony version - I use 1.4.11 where you'll have this functionality working from scratch.
If this is not possible in your case, the best place to this will be to override the base doSave() method in candidateForm like this:
protected function doSave($con = null)
{
$existing = $this->object->Position->getPrimaryKeys();
$values = $this->getValue('candidate_positions');
$unlink = array_diff($existing, $values);
$this->object->unlink('Position', array_values($unlink));
$link = array_diff($values, $existing);
$this->object->link('Position', array_values($link));
parent::doSave($con);
}
and also you'll probably need to manually set the selected link object on form load like this:
public function updateDefaultsFromObject()
{
parent::updateDefaultsFromObject();
if (isset($this->widgetSchema['candidate_positions']))
{
$this->setDefault('candidate_positions', $this->object->Position->getPrimaryKeys());
}
}
This should do the trick.
UPDATE
I think that since the update to 1.4.11 doesn't help, there is some problem with the schema definition and my guess is that you need to add relation definition to link table 'candidatePosition' like this:
relations:
Candidate:
class: Candidate
local: candidate_id
foreign: id
foreignAlias: Candidates
Position:
class: Position
local: position_id
foreign: id
foreignAlias: Positions
Hope this helps.
Regards.
Upvotes: 0
Reputation: 12312
About 10 month ago I had similar trouble with M:N relationships and meta-data in the join table.
I found those blog entries of melikedev very useful! This is not exactly the same as your use case, but it might give you some hints:
http://melikedev.com/2009/12/09/symfony-w-doctrine-saving-many-to-many-mm-relationships/
http://melikedev.com/2009/12/06/symfony-sfformextraplugin-select-double-list-maintain-order/
http://melikedev.com/2010/04/06/symfony-saving-metadata-during-form-save-sort-ids/
Upvotes: 1