[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