Commit 1344f440 authored by lecluse's avatar lecluse
Browse files

Refonte des services avec la création d'une classe abstraite réservée aux entités.

Factorisation du code et ajout de la possibilité de filtrer des listes d'entités depuis les services sur n'importe quelle propriété.
parent cfb50315
......@@ -64,7 +64,7 @@ class EtablissementController extends AbstractActionController
return new JsonModel(array());
}
$entities = $this->getServiceEtablissement()->findByLibelle($term)->getQuery()->execute();
$entities = $this->getServiceEtablissement()->finderByLibelle($term)->getQuery()->execute();
$result = array();
foreach ($entities as $item) { /* @var $item \Application\Entity\Db\Etablissement */
......
......@@ -34,28 +34,21 @@ class ServiceController extends AbstractActionController
$context = $service->getGlobalContext();
$qb = $service->finderByContext($context);
$annee = $context['annee'];
$services = $service->getList($qb);
if (empty($this->context['intervenant'])){
$rechercheForm = $this->getServiceLocator()->get('FormElementManager')->get('ServiceRecherche');
/* @var $rechercheForm \Application\Form\Service\Recherche */
if ($this->getRequest()->isPost()){
$rechercheForm->setData( $this->getRequest()->getPost() );
}
$rechercheForm->populateOptions($services);
$filter = new \StdClass;
$rechercheForm->bind($filter);
$rechercheForm->setData( $this->getRequest()->getQuery() );
if ($rechercheForm->isValid()){
if (isset($filter->intervenant) && $filter->intervenant){
$service->finderByIntervenant( $filter->intervenant, $qb );
}
if (isset($filter->elementPedagogique) && $filter->elementPedagogique){
$service->finderByElementPedagogique( $filter->elementPedagogique, $qb );
}
$service->finderByFilterObject($filter, null, $qb);
$services = $service->getList($qb); // on rafraichit en fonction des filtres
}
}else{
$rechercheForm = null; // pas de filtrage
}
$services = $qb->getQuery()->execute();
$errors = null;
/* Bertrand: services référentiels */
......
......@@ -11,7 +11,7 @@ class ElementPedagogique
{
public function __toString()
{
return $this-getSourceCode().' - '.$this->getLibelle();
return $this->getSourceCode().' - '.$this->getLibelle();
}
/**
......
......@@ -3,12 +3,11 @@
namespace Application\Form\Service;
use Zend\Form\Form;
use UnicaenApp\Form\Element\SearchAndSelect;
use Zend\Stdlib\Hydrator\ClassMethods;
use Zend\Form\Element\Select;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
use Application\Form\OffreFormation\ElementPedagogiqueRechercheFieldset;
use Application\Entity\Db\Service;
/**
......@@ -20,25 +19,30 @@ class Recherche extends Form implements InputFilterProviderInterface, ServiceLoc
{
use ServiceLocatorAwareTrait;
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
$this ->setAttribute('method', 'post')
$this ->setAttribute('method', 'get')
->setAttribute('class', 'service-recherche')
;
$intervenant = new SearchAndSelect('intervenant');
$intervenant ->setRequired(false)
->setSelectionRequired(true)
->setLabel("Intervenant :")
->setAttributes(array('title' => "Saisissez le nom suivi éventuellement du prénom (2 lettres au moins)"));
$intervenant = new Select('intervenant');
$intervenant->setLabel('Intervenant :');
$this->add($intervenant);
$element = new ElementPedagogiqueRechercheFieldset('elementPedagogique');
$element = new Select('element-pedagogique');
$element->setLabel('Enseignement ou responsabilité :');
$this->add($element);
$etape = new Select('etape');
$etape->setLabel('Formation :');
$this->add($etape);
$structureEns = new Select('structure-ens');
$structureEns->setLabel('Structure d\'enseignement :');
$this->add($structureEns);
/**
* Submit
*/
......@@ -46,12 +50,47 @@ class Recherche extends Form implements InputFilterProviderInterface, ServiceLoc
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Filtrer',
'value' => 'Afficher',
'class' => 'btn btn-primary',
),
));
}
/**
*
* @param Service[] $services
*/
public function populateOptions( $services )
{
$intervenants = array();
$elements = array();
$etapes = array();
$structuresEns = array();
foreach( $services as $service ){
if ($intervenant = $service->getIntervenant()){
$intervenants[$intervenant->getId()] = (string)$intervenant;
}
if ($structureEns = $service->getStructureEns()){
$structuresEns[$structureEns->getId()] = (string)$structureEns;
}
if ($element = $service->getelementPedagogique()){
$elements[$element->getId()] = (string)$element;
$etape = $element->getEtape();
$etapes[$etape->getId()] = (string)$etape;
}
}
asort( $intervenants );
asort( $elements );
asort( $etapes );
asort( $structuresEns );
$this->get('intervenant')->setValueOptions( array('' => '(Tous)') + $intervenants );
$this->get('element-pedagogique')->setValueOptions( array('' => '(Tous)') + $elements );
$etapeSelect = $this->get('etape')->setValueOptions( array('' => '(Toutes)') + $etapes );
$structureEnsSelect = $this->get('structure-ens')->setValueOptions( array('' => '(Toutes)') + $structuresEns );
}
/**
* Should return an array specification compatible with
* {@link Zend\InputFilter\Factory::createInputFilter()}.
......@@ -64,7 +103,13 @@ class Recherche extends Form implements InputFilterProviderInterface, ServiceLoc
'intervenant' => array(
'required' => false
),
'elementPedagogique' => array(
'etape' => array(
'required' => false,
),
'structure-ens' => array(
'required' => false,
),
'element-pedagogique' => array(
'required' => false,
),
);
......
......@@ -24,14 +24,6 @@ class RechercheFactory implements FactoryInterface
{
$recherche = new Recherche();
$recherche->setServiceLocator($serviceLocator);
$recherche->init();
/* @var $serviceLocator \Zend\Form\FormElementManager */
$url = $serviceLocator->getServiceLocator()->get('viewhelpermanager')->get('url');
$recherche->get('intervenant')->setAutocompleteSource( $url('recherche', array('action' => 'intervenantFind')) );
$recherche->setAttribute('action', $url(null, array(), array(), true));
$h = $serviceLocator->getServiceLocator()->get('FormServiceRechercheHydrator');
$recherche->setHydrator($h);
......
......@@ -27,19 +27,17 @@ class RechercheHydrator implements HydratorInterface, ServiceLocatorAwareInterfa
$em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
/* @var $em \Doctrine\ORM\EntityManager */
$id = (int)$data['elementPedagogique']['element']['id'];
if ($id){
$object->elementPedagogique = $em->find('Application\Entity\Db\ElementPedagogique', $id);
}else{
$object->elementPedagogique = null;
}
$id = (int)$data['intervenant']['id'];
if ($id){
$object->intervenant = $em->getRepository('Application\Entity\Db\Intervenant')->findOneBySourceCode($id);
}else{
$object->intervenant = null;
}
$id = isset($data['element-pedagogique']) ? (int)$data['element-pedagogique'] : null;
$object->elementPedagogique = $id ? $em->find('Application\Entity\Db\ElementPedagogique', $id) : null;
$id = isset($data['etape']) ? (int)$data['etape'] : null;
$object->etape = $id ? $em->find('Application\Entity\Db\Etape', $id) : null;
$id = isset($data['structure-ens']) ? (int)$data['structure-ens'] : null;
$object->structureEns = $id ? $em->find('Application\Entity\Db\Structure', $id) : null;
$id = isset($data['intervenant']) ? (int)$data['intervenant'] : null;
$object->intervenant = $id ? $em->find('Application\Entity\Db\Intervenant', $id) : null;
return $object;
}
......@@ -51,7 +49,12 @@ class RechercheHydrator implements HydratorInterface, ServiceLocatorAwareInterfa
*/
public function extract($object)
{
$data = array();
$data = array(
'intervenant' => isset($object->intervenant) && $object->intervenant ? $object->intervenant->getId() : null,
'element-pedagogique' => isset($object->elementPedagogique) && $object->elementPedagogique ? $object->elementPedagogique->getId() : null,
'etape' => isset($object->etape) && $object->etape ? $object->etape->getId() : null,
'structure-ens' => isset($object->structureEns) && $object->structureEns ? $object->structureEns->getId() : null,
);
return $data;
}
......
<?php
namespace Application\Service;
use Doctrine\ORM\EntityRepository;
use Zend\Stdlib\Hydrator\HydratorInterface;
use Zend\Stdlib\Hydrator\ObjectProperty;
use Doctrine\ORM\QueryBuilder;
use UnicaenApp\Exception\RuntimeException;
/**
*
*
* @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
*/
abstract class AbstractEntityService extends AbstractService
{
/**
* EntityRepository
*
* @var EntityRepository
*/
private $repo;
/**
*
* @var \ReflectionClass
*/
private $reflectionClass;
/**
*
* @var boolean
*/
private $hasHistorique;
/**
* Liste des propriétés des entités
*
* @var string[]
*/
private $properties;
/**
* retourne la classe des entités
*
* @return string
* @throws RuntimeException
*/
abstract public function getEntityClass();
/**
* Retourne l'alias d'entité courante
*
* @return string
*/
abstract public function getAlias();
/**
* @return \ReflectionClass
*/
private function getReflectionClass(){
if (! $this->reflectionClass){
$this->reflectionClass = new \ReflectionClass( $this->getEntityClass() );
}
return $this->reflectionClass;
}
/**
* Retourne le repos des entités et active le filtre historique si besoin
*
* @return EntityRepository
*/
public function getRepo()
{
if( !$this->repo ){
if ($this->hasHistorique()) $this->getEntityManager()->getFilters()->enable("historique");
$this->repo = $this->getEntityManager()->getRepository($this->getEntityClass());
}
return $this->repo;
}
/**
* Détermine si les entités gèrent les historiques ou non
*
* @return boolean
*/
public function hasHistorique()
{
if (null === $this->hasHistorique){
$this->hasHistorique = $this->getReflectionClass()->implementsInterface('Application\Entity\Db\HistoriqueAwareInterface');
}
return $this->hasHistorique;
}
/**
* Retourne la liste des propriétés filtrables de l'entité
*
* @return string[]
*/
public function getProperties()
{
if (null === $this->properties){
$m = $this->getReflectionClass()->getMethods(\ReflectionMethod::IS_PUBLIC);
$p = $this->getReflectionClass()->getProperties(\ReflectionProperty::IS_PRIVATE);
$methods = array();
foreach( $m as $method ){
if (0 === strpos($method->name, 'get')) $methods[] = $method->name;
}
$this->properties = array();
foreach( $p as $property ){
if (in_array('get'.ucfirst($property->name),$methods)){
$this->properties[] = $property->name;
}
}
}
return $this->properties;
}
/**
* Initialise une requête
* Permet de retourner des valeurs par défaut ou de les forcer en cas de besoin
* Format de sortie : array( $qb, $alias ).
*
* @param QueryBuilder|null $qb Générateur de requêtes
* @param string|null $alias Alias d'entité
* @return array
*/
public function initQuery(QueryBuilder $qb=null, $alias=null)
{
if (null === $alias) $alias = $this->getAlias();
if (empty($qb)) $qb = $this->getRepo()->createQueryBuilder($alias);
return array($qb,$alias);
}
/**
* Retourne une liste d'entités en fonction du QueryBuilder donné
*
* La liste de présente sous la forme d'un tableau associatif, dont les clés sont les ID des entités et les valeurs les entités elles-mêmes
*
* @param QueryBuilder|null $qb
* @param string|null $alias
* @return array
*/
public function getList(QueryBuilder $qb=null, $alias=null )
{
list($qb,$alias) = $this->initQuery($qb, $alias);
$entities = $qb->getQuery()->execute();
$result = array();
foreach( $entities as $entity ){
$result[$entity->getId()] = $entity;
}
return $result;
}
/**
* Filtre une liste d'entités à partir d'un objet chargé de lui préciser les filtres à appliquer
* Si l'hydrateur n'est pas précisé et que l'objet implémente HydratorAwareInterface, alors l'hydrateur de l'objet est utilisé.
* Si lhydrateur n'a toujours pas pu être déterminé, alors l'hydrateur ObjectProperty est utilisé.
*
* @param \StdClass $object Objet contenant les filtres
* @param HydratorInterface $hydrator Hydrateur
* @param QueryBuilder $qb QueryBuilder à ne pas recréer. Permet de chaîner les filtres
* @param string $alias Alias d'entité à utiliser par défaut. Utile en cas de jointure
* @return QueryBuilder Retourne le QueryBuilder, pour chaîner les filtres au besoin
*/
public function finderByFilterObject( \StdClass $object, HydratorInterface $hydrator=null, QueryBuilder $qb=null, $alias=null )
{
if (! $hydrator && $object instanceof \Zend\Stdlib\Hydrator\HydratorAwareInterface){
$hydrator = $object->getHydrator();
}
if (! $hydrator){
$hydrator = new ObjectProperty();
}
return $this->finderByFilterArray($hydrator->extract($object), $qb, $alias);
}
/**
* Filtre une liste d'entités à partir d'untableau associatif dont les clés sont les propriétés à filtrer et les valeurs les données à filtrer.
* Le filtre ne prend pas en compte les valeurs nulles
*
* @param array $properties Liste des propriétés à filtrer
* @param QueryBuilder $qb QueryBuilder à ne pas recréer. Permet de chaîner les filtres
* @param string $alias Alias d'entité à utiliser par défaut. Utile en cas de jointure
* @return QueryBuilder Retourne le QueryBuilder, pour chaîner les filtres au besoin
*/
public function finderByFilterArray( array $properties, QueryBuilder $qb=null, $alias=null )
{
list($qb,$alias) = $this->initQuery($qb, $alias);
foreach( $properties as $property => $value){
if (null != $value){
if(method_exists($this,'finderBy'.ucfirst($property))){
call_user_func(array($this,'finderBy'.ucfirst($property)), $value, $qb, $alias);
}elseif (in_array($property, $this->getProperties())){ // ne traite que les propriétés reconnues, ignore les autres
$this->finderByProperty($property, $value, $qb);
}
}
}
return $qb;
}
/**
*
* @param string $property Nom de la propriété à filtrer
* @param mixed $value Valeur du filtre
* @param QueryBuilder $qb QueryBuilder à ne pas recréer. Permet de chaîner les filtres
* @param string|null $alias Alias d'entité à utiliser par défaut. Utile en cas de jointure
* @return \Doctrine\ORM\QueryBuilder Retourne le QueryBuilder, pour chaîner les filtres au besoin
*/
public function finderByProperty( $property, $value, QueryBuilder $qb=null, $alias=null )
{
list($qb,$alias) = $this->initQuery($qb, $alias);
$qb->andWhere("$alias.$property = :$property")->setParameter($property, $value);
return $qb;
}
public function __call($name, $arguments)
{
if (0 === strpos($name, 'finderBy')){
$property = lcfirst(substr($name, 8));
$value = isset($arguments[0]) ? $arguments[0] : null;
$qb = isset($arguments[1]) ? $arguments[1] : null;
$alias = isset($arguments[2]) ? $arguments[2] : null;
return $this->finderByProperty($property, $value, $qb, $alias);
}
}
}
\ No newline at end of file
......@@ -2,8 +2,6 @@
namespace Application\Service;
use Application\Service\AbstractService;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\Expr\Func;
......@@ -14,16 +12,28 @@ use Doctrine\ORM\Query\Expr\Func;
*
* @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
*/
class Etablissement extends AbstractService
class Etablissement extends AbstractEntityService
{
/**
* Repository
* retourne la classe des entités
*
* @var Repository
* @return string
* @throws RuntimeException
*/
protected $repo;
public function getEntityClass()
{
return 'Application\Entity\Db\Etablissement';
}
/**
* Retourne l'alias d'entité courante
*
* @return string
*/
public function getAlias(){
return 'etab';
}
......@@ -34,16 +44,16 @@ class Etablissement extends AbstractService
* @param QueryBuilder|null $queryBuilder
* @return QueryBuilder
*/
public function findByLibelle($term, QueryBuilder $qb=null)
public function finderByLibelle($term, QueryBuilder $qb=null, $alias=null)
{
$terms = explode( ' ', $term );
if (empty($qb)) $qb = $this->getRepo()->createQueryBuilder('e');
list($qb,$alias) = $this->initQuery($qb, $alias);
$concatFields = array(
'e.libelle',
'e.departement',
'e.localisation',
"$alias.libelle",
"$alias.departement",
"$alias.localisation",
);
foreach ($concatFields as $field) {
......@@ -69,22 +79,9 @@ class Etablissement extends AbstractService
$qb->andWhere($qb->expr()->like($qb->expr()->upper($haystack), $qb->expr()->upper("CONVERT(?$index, ?1)")));
$index++;
}
$qb->orderBy('e.libelle');
$qb->orderBy("$alias.libelle");
$qb->setParameters( $parameters );
return $qb;
}
/**
*
* @return EntityRepository
*/
public function getRepo()
{
if( empty($this->repo) ){
$this->getEntityManager()->getFilters()->enable("historique");
$this->repo = $this->getEntityManager()->getRepository('Application\Entity\Db\Etablissement');
}
return $this->repo;
}
}
\ No newline at end of file
......@@ -2,8 +2,6 @@
namespace Application\Service;
use Application\Service\AbstractService;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Application\Entity\Db\MotifNonPaiement as Entity;
......@@ -12,16 +10,9 @@ use Application\Entity\Db\MotifNonPaiement as Entity;
*
* @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
*/
class MotifNonPaiement extends AbstractService
class MotifNonPaiement extends AbstractEntityService
{
/**
* Repository
*
* @var Repository
*/
protected $repo;
/**
* Liste des motifs de non paiement
*
......@@ -30,34 +21,37 @@ class MotifNonPaiement extends AbstractService
protected $motifsNonPaiement;