Reputation: 20456
A user makes public and/or private notes about an expertise record. These notes are displayed in the sidebar of the page that shows the expertise record. The notes are grouped into 2 sets: the current user's notes (both public and private) and the public notes from other users. All the current user's notes are editable and deletable. Each delete icon is a link to a route. Each edit icon is a javascript toggle to display a form for the particular note (the edit form looks like the add form except it's populated). Only one note should be added/edited or deleted per submit action. Submission is not through ajax. The page looks something like this:
How can I display and process an independent form for both a 'New Note' and each of the notes in the 'My Notes' section?
If I embed a collection of NoteType forms in an ExpertiseType form, processing is correct but I cannot seem to group the forms into 'Add a Note', 'My Notes' and 'Public Notes' sections. And worse, public notes are editable. I've abandoned this approach since there is not easy way to filter which notes have forms.
If I subset the notes in the controller and create a form for each one, every note is processed as though it is a new note unless I use named forms. With named forms, new notes are correctly created. But all edits are applied to the most recent note, and all the other notes have their privacy and text fields set to the column default. How can I get around this?
{% block sidebar %}
<div id='sidebar'><h2>Profile Notes</h2>
{% for flashMessage in app.session.flashbag.get('note_success') %}
<div class="flash-success">
{{ flashMessage }}
</div>
{% endfor %}
<fieldset class='wrapper'><legend>Add a note</legend>
{{ form(form) }}
</fieldset>
{% if usernotes|length > 0 %}
<fieldset><legend>My notes</legend>
<dl>
{% for note in usernotes%}
<dt>{{ note.moddate|date("Y-m-d H:i") }}
<a class='actionlink' href='{{ path('exp_delnote', {'id':note.id}) }}' title="delete"><img src="{{ asset('images/silk_icons/delete.png') }}" alt="delete note"></a>
<a class='actionlink inlineedit' href='#' data-editform="{{ form(note.myform)|e }}" title="edit note"><img src="{{ asset('images/silk_icons/pencil.png') }}" alt="edit note"></a>
</dt>
<dd class='note_{{ note.private ? 'priv' : 'pub' }}' title='{{ note.private ? 'Private Note' : 'Public Note'}}'>{{ note.note }}</dd>
{% endfor %}
</dl>
</fieldset>
{% endif %}
{% if publicnotes|length > 0 %}
<fieldset><legend>Public notes</legend>
<dl>
{% for note in publicnotes%}
<dt>{{ note.moddate|date("Y-m-d H:i") }}, {{ note.person|expadinfo("fullname") }}</dt>
<dd>{{ note.note }}</dd>
{% endfor %}
</dl>
</fieldset>
{% endif %}
</div>
{% endblock %}
There are 3 entity classes in play: Expertise
, Note
and Person
. The Expertise
entity has a collection of Notes
. Each Note
is associated with a Person
(its "owner") and an Expertise
record. Entities are managed by Doctrine.
This successfully creates new notes. But no matter which note I edit, the changes are applied to the most recent note and the other editable notes have their text and privacy fields set to the column default.
public function profileAction($uname){
$request = $this->get('request');
$adsearcher = $this->get('exp_adsearcher');
$userrecord = $adsearcher->getRecordByUserName($uname);
if(!$userrecord) throw $this->createNotFoundException('No such person exists.');
$userrecord->setLocale($request->get('_locale'));
$person = $adsearcher->getPersonByRecord($userrecord);
$expertise = $this->getExpertiseEntity($person);
$curuser = $adsearcher->getPersonByUserName($this->getUser()->getUsername());
//add a new note, and trying to use a named form
$newnote = $this->getNewNote($expertise, $curuser);
$addnoteform = $this->get('form.factory')->createNamedBuilder('newnote', new NoteType(), $newnote)->getForm();
$addnoteform->handleRequest($request);
if($addnoteform->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($newnote);
$em->flush();
$this->get('session')->getFlashBag()->add(
'note_success',
$this->get('translator')->trans('message.note.add.success')
);
$addnoteform = $this->createForm(new NoteType, $this->getNewNote($expertise, $curuser)); //so that add form is always empty
}
//get all the editable notes
$usernotes = $expertise->getUserNotes($curuser);
foreach($usernotes as $note){
$form = $this->get('form.factory')->createNamedBuilder('note'.$note->getID(), new NoteType(), $note)->getForm();
$form->handleRequest($request);
if($form->isValid()){
$msg = 'message.note.edit.success';
$em = $this->getDoctrine()->getManager();
$em->persist($expertise);
$em->flush();
$this->get('session')->getFlashBag()->add(
'note_success',
$this->get('translator')->trans('message.note.edit.success')
);
return $this->redirect($this->generateUrl('exp_profile', array('uname'=>$uname)));
}
$note->myform = $form->createView();
}
//get all the public notes, not editable
$publicnotes = $expertise->getPublicNotes($curuser, $adsearcher);
return array('userrecord'=> $userrecord, 'expertise'=>$expertise,'usernotes'=>$usernotes, 'publicnotes'=>$publicnotes, 'form'=>$addnoteform->createView());
}
Upvotes: 0
Views: 1293
Reputation: 48865
Lets see if I can be more helpful this time.
Make yourself a NotesType which will hold zero or more notes.
class NotesType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('notes', 'collection', array('type' => new NoteType());
Your controller needs to make two forms, one for the new note and one for the existing notes
public function profileAction($uname)
{
// One form for a new note
$newNote = $this->getNewNote($expertise, $curuser);
$newNoteForm = $this->createForm(new NoteType(), $newNote);
// One form for existing notes
$userNotes = $expertise->getUserNotes($curuser);
$userNotesData = array('notes' => $userNotes);
$userNotesForm = $this->createForm(new NotesType(), $userNotesData);
// Do NOT attempt to process the forms here
// Just pass onto the template
$tplData = array();
$tplData['newNoteForm'] = $newNoteForm->createView();
$tplData['userNotesForm'] = $userNotesForm->createView();
return $tplData;
So now we have two independent forms, one for the new note and one for the list of existing notes. Adjust your template accordingly. You will want the newNoteForm to post to a different action than the userNotesForm. That will allow you to process each form independently. After processing the form you would then redirect back to profileAction.
public function postUserNotesAction()
{
// One form for existing notes
$userNotes = $expertise->getUserNotes($curuser);
$userNotesData = array('notes' => $userNotes);
$userNotesForm = $this->createForm(new NotesType(), $userNotesData);
$userNotesForm->handleRequest($request);
if($userNotesForm->isValid())
{
$userNotesData = $userNotesForm->getData();
$userNotes = $userNotesData['notes'];
$em->flush();
// Redirect
Upvotes: 1