[FLOW3-general] How to set/get/show M:N relations using checkboxes instead of selectbox?

Jan Greth jan at greth.me
Mon Mar 11 09:45:09 CET 2013


Thanks Guys for your Help.
I made it with a ViewHelper similar to yours Stephen and some hints from 
"akii" and "afoeder" in the #typo3-flow IRC Chat.

My now working Code is the following. The Next Days I'll do a Blogpost 
too - but this will be in german... :)

So, thanks again (also to akii and afoeder) and here now my code:
(For nicer formatting visit:
http://pastebin.com/CZAzCWmU
http://pastebin.com/CpKsapSH
http://pastebin.com/6gjbghhs
http://pastebin.com/77m0cnu1
http://pastebin.com/bVfsRkdA )



#####  Event Model #####

<?php
namespace Jpg\Events\Domain\Model;

/*                                                                        *
  * This script belongs to the TYPO3 Flow package "Jpg.Events".            *
  *                                                                        *
  * 
    */

use TYPO3\Flow\Annotations as Flow;
use Doctrine\ORM\Mapping as ORM;

/**
  * A Event
  *
  * @Flow\Entity
  */
class Event {

     /**
      * The title
      * @var string
      * @Flow\Validate(type="NotEmpty")
      * @Flow\Validate(type="String")
      * @Flow\Validate(type="StringLength", options={ "minimum"=5, 
"maximum"=100 })
      */
     protected $title;

     /**
     * @var \Jpg\Events\Domain\Model\Location
     * @ORM\ManyToOne
     */
     protected $location;

     /**
     * @var 
\Doctrine\Common\Collections\Collection<\Jpg\Events\Domain\Model\Person>
     * @ORM\ManyToMany(inversedBy="events")
     */
     protected $persons;

     /**
      * Constructs this event
      */
     public function __construct() {
         $this->persons = new 
\Doctrine\Common\Collections\ArrayCollection();
     }

     /**
      * Get the Event's title
      *
      * @return string The Event's title
      */
     public function getTitle() {
         return $this->title;
     }

     /**
      * Sets this Event's title
      *
      * @param string $title The Event's title
      * @return void
      */
     public function setTitle($title) {
         $this->title = $title;
     }

     /**
     * setter for location
     *
     * @param \Jpg\Events\Domain\Model\Location $location
     * @return void
     */
     public function setLocation($location) {
         $this->location = $location;
     }

     /**
     * getter for location
     *
     * @return \Jpg\Events\Domain\Model\Location
     */
     public function getLocation() {
         return $this->location;
     }

     /**
      * Setter for person
      *
      * @param 
\Doctrine\Common\Collections\Collection<\Jpg\Events\Domain\Model\Person> 
$persons The attending persons
      * @return void
      */
     public function setPersons(\Doctrine\Common\Collections\Collection 
$persons) {
         $this->persons = clone $persons;
     }

     /**
      * Adds a person to this event
      *
      * @param \Jpg\Events\Domain\Model\Person $person
      * @return void
      */
     public function addPerson(\Jpg\Events\Domain\Model\Person $person) {
         $this->persons->add($person);
     }

     /**
      * Removes a person to this event
      *
      * @param \Jpg\Events\Domain\Model\Person $person
      * @return void
      */
     public function removePerson(\Jpg\Events\Domain\Model\Person $person) {
         $this->persons->remove($person);
     }

     /**
      * Getter for persons
      *
      * @return 
\Doctrine\Common\Collections\Collection<\Jpg\Events\Domain\Model\Person> 
The attending persons
      */
     public function getPersons() {
         return clone $this->persons;
     }

}
?>

#####  Person Model #####

<?php
namespace Jpg\Events\Domain\Model;

/*                                                                        *
  * This script belongs to the TYPO3 Flow package "Jpg.Events".           *
  *                                                                        *
  * 
    */

use TYPO3\Flow\Annotations as Flow;
use Doctrine\ORM\Mapping as ORM;

/**
  * A Person
  *
  * @Flow\Entity
  */
class Person {

     /**
      * The name
      * @var string
      */
     protected $name;

     /**
     * The events the person is attending
     * @var 
\Doctrine\Common\Collections\Collection<\Jpg\Events\Domain\Model\Event>
     * @ORM\ManyToMany(mappedBy="persons")
     */
     protected $events;


     /**
      * Constructs this tag
      */
     public function __construct() {
         $this->events = new \Doctrine\Common\Collections\ArrayCollection();
     }

     /**
      * Get the Person's name
      *
      * @return string The Person's name
      */
     public function getName() {
         return $this->name;
     }

     /**
      * Sets this Person's name
      *
      * @param string $name The Person's name
      * @return void
      */
     public function setName($name) {
         $this->name = $name;
     }

}
?>


#####  EventController #####

<?php
namespace Jpg\Events\Controller;

/*                                                                        *
  * This script belongs to the TYPO3 Flow package "Jpg.Events".           *
  *                                                                        *
  * 
    */

use TYPO3\Flow\Annotations as Flow;

/**
  * Event controller for the Jpg.Events package
  *
  * @Flow\Scope("singleton")
  */
class EventController extends \TYPO3\Flow\Mvc\Controller\ActionController {

     /**
     * @var Jpg\Events\Domain\Repository\EventRepository
     * @Flow\Inject
     */
     protected $eventRepository;

     /**
     * @var Jpg\Events\Domain\Repository\LocationRepository
     * @Flow\Inject
     */
     protected $locationRepository;

     /**
     * @var Jpg\Events\Domain\Repository\PersonRepository
     * @Flow\Inject
     */
     protected $personRepository;

     /**
      * @return void
      */
     public function listAction() {
         $eventList = $this->eventRepository->findAll();
         $this->view->assign('events', $eventList);
     }

     /**
      * Index action
      *
      * @return void
      */
     public function indexAction() {
     }

     /**
      * newAction
      * this action will only load the template and display the
      * form for adding a new event
      *
      * @return void
      */
     public function newAction() {
         $newEvent = new \Jpg\Events\Domain\Model\Event();
         $this->view->assign('newEvent', $newEvent);
         $this->view->assign('locations', 
$this->locationRepository->findAll());
         $this->view->assign('persons', $this->personRepository->findAll());
     }

     /**
      * @param \Jpg\Events\Domain\Model\Event $events
      * @return void
      */
     public function createAction(\Jpg\Events\Domain\Model\Event $event) {
         $this->eventRepository->add($event);
         $this->redirect('list');
     }

     /**
      * Shows a form for editing an existing location object
      *
      * @param \Jpg\Events\Domain\Model\Event $event The event to edit
      * @return void
      */
     public function editAction(\Jpg\Events\Domain\Model\Event $event) {
         $this->view->assign('event', $event);
         $this->view->assign('locations', 
$this->locationRepository->findAll());
         $this->view->assign('persons', 
$this->personRepository->findAll() );
     }

     /**
      * Updates the given location object
      *
      * @param \Jpg\Events\Domain\Model\Event $event The event to update
      * @return void
      */
     public function updateAction(\Jpg\Events\Domain\Model\Event $event) {
         $this->eventRepository->update($event);
         $this->addFlashMessage('Updated the event.');
         $this->redirect('list');
     }

     /**
      * Deletes an existing event
      *
      * @param \Jpg\Events\Domain\Model\Event $event The event to remove
      * @return void
      */
     public function deleteAction(\Jpg\Events\Domain\Model\Event $event) {
         $this->eventRepository->remove($event);
         $this->addFlashMessage('The event has been deleted.');
         $this->redirect('list');
     }

     /**
      * @return \TYPO3\Flow\Error\Message
      */
     protected function getErrorFlashMessage() {
      switch ($this->actionMethodName) {
       case 'createAction' :
        return new \TYPO3\Flow\Error\Message('Could not save form, 
because some fields are not filled out correctly');
       default:
        return parent::getErrorFlashMessage();
      }
     }
}
?>

#####  Event Edit View #####

{namespace ev=Jpg\Events\ViewHelpers}
<f:layout name="Default" />

<f:section name="content">Edit event "{event.title}"

<f:comment><!-- Display validation errors & flash Messages --></f:comment>
<f:flashMessages class="flashmessages"/>
<f:form.validationResults for="location">
     <f:if condition="{validationResults.flattenedErrors}">
         <div class="error">
             <dl>
                 <f:for each="{validationResults.flattenedErrors}" 
key="propertyPath" as="errors">
                     <dt>{propertyPath}</dt>
                         <dd>
                             <ul>
                                 <f:for each="{errors}" as="error">
                                     <li>{error}</li>
                                 </f:for>
                             </ul>
                         </dd>
                 </f:for>
             </dl>
         </div>
     </f:if>
</f:form.validationResults>
<f:comment><!-- Display validation errors & flash Messages end 
--></f:comment>

     <f:form action="update" controller="Event" package="Jpg.Events" 
name="event" object="{event}">
         <label for="title">Title:</label><f:form.textfield id="title" 
property="title" />
         <br />

         <label for="location">Location:</label>
             <f:form.select property="location" id="location" 
options="{locations}" optionLabelField="name" />
         <br />

         <f:comment> <!-- Working Selctbox -->
         <label for="persons">Persons:</label>
             <f:form.select property="persons" id="persons" 
options="{persons}" optionLabelField="name" multiple="multiple" size="3"/>
         <br />
         </f:comment>

         <f:for each="{persons}" as="person">
             <ev:inArray haystack="{event.persons}" needle="{person}" >
                 <f:then>
                     <f:form.checkbox property="persons" 
value="{f:format.identifier(value: person)}" checked="true" 
multiple="true"/><label>{person.name}</label><br/>
                 </f:then>
                 <f:else>
                     <f:form.checkbox property="persons" 
value="{f:format.identifier(value: person)}" 
multiple="true"/><label>{person.name}</label><br/>
                 </f:else>
             </ev:inArray>
         </f:for>

         <f:form.submit value="Speichern" />
     </f:form>
</f:section>

#####  InArrayViewHelper.php #####

namespace Jpg\Events\ViewHelpers;

use TYPO3\Flow\Annotations as Flow;

/**
  * In Array View Helper
  *
  * For Example you could use it with multiple checkboxes to check the 
selected one:
  *
  *<f:for each="{persons}" as="person">
  *	<ev:inArray haystack="{event.persons}" needle="{person}" >
  *		<f:then>
  *			<f:form.checkbox property="persons" value="{person}" 
checked="true" /><label>{person.name}</label><br/>
  *		</f:then>
  *		<f:else>
  *			<f:form.checkbox property="persons" value="{person}" 
/><label>{person.name}</label><br/>
  *		</f:else>
  *	</ev:inArray>
  *</f:for>
  *
  *
  * @Flow\Scope("prototype")
  */
class InArrayViewHelper extends 
\TYPO3\Fluid\Core\ViewHelper\AbstractConditionViewHelper {

     public function initializeArguments() {
     parent::initializeArguments();
     $this->registerArgument('haystack', 'mixed', 'View helper haystack 
', TRUE);
     $this->registerArgument('needle', 'string', 'View helper needle', 
TRUE);
     }

     /**
     * Check if value is in array
     *
     *
     * @param string	$needle
     * @param array	$haystack
     *
     * @return boolean
     */
     public function render() {

         $needle = $this->arguments['needle'];
         $haystack = $this->arguments['haystack'];

         if(is_string($haystack)) {
             $haystack = strpos($haystack, ',') ? explode(',',$haystack) 
: $haystack;
         }
         if(is_object($haystack)) {
             $haystack = $haystack->toArray();
         }

         if(in_array($needle, $haystack) && is_array($haystack)) {
             return $this->renderThenChild();
         } else {
             return $this->renderElseChild();
         }
     }
}

?>


More information about the FLOW3-general mailing list