2010-11-18 36 views
2

Le client doit modifier l'ordre des articles comme il veut, ce qui signifie que j'ai besoin d'une colonne "ordre" ou "séquence" pour sauvegarder la position réelle de chaque article. Comment puis-je implémenter ceci en utilisant Doctrine 2?Doctrine 2 Sortable

+0

Avez-vous réussi à trouver une solution? Je cherche cela maintenant et je me demandais ce que vous avez fini par utiliser? – DavidW

Répondre

0

Ajouter une autre colonne:

 
    sort_order: 
     type: integer(4) 
     default: 10 

Maintenant, le changement que l'entrée de 10 à quoi que ce soit se déplace d'autre dans le resultset qui serait récupéré par quelque chose comme:

 
$this->result = Doctrine_Core::getTable('CustomerTable') 
    ->createQuery('a')->orderBy("a.sort_order asc, a.id desc")->execute(); 
+0

Non. Pas ce que je voulais dire. J'ai l'ordre comme 1, 2, 3, 4, 5. Maintenant l'utilisateur (en utilisant le glisser-déposer) déplace l'article 2 à la position 4 par exemple, donc j'ai besoin de faire des changements comme ceci: 2-> 4, 3-> 2, 4-> 3 – enumag

+0

Vous avez toujours besoin d'une colonne de tri pour stocker cet ordre. Je définirais la colonne de tri pour avoir l'ID de clé primaire par défaut et plus tard échanger les valeurs de colonne de tri lorsque vous les faites glisser autour. – dsomnus

+0

De couse, mais comment faire ces changements automatiquement? Idéal serait de simplement définir $ entity-> order = 5; $ em-> flush(); et les autres entités devraient changer automatiquement. – enumag

0

Vous pouvez récupérer automatiquement les collections commandées par un champ donné:

http://www.doctrine-project.org/projects/orm/2.0/docs/reference/association-mapping/en#ordering-to-many-collections

De cette façon, vous pouvez définir la commande en utilisant la colonne "sort_order". Ensuite, vous avez besoin de méthodes permettant de mettre à jour les colonnes de tri correctement en utilisant les instructions de mise à jour DQL pour des raisons de performances, par exemple "moveTo ($ pos, $ entity)" et en indiquant le nombre de mises à jour. Mais cela résout la moitié de votre problème si vous devez itérer la collection dans la même requête dans la mesure où l'ordre est modifié. C'est cependant un cas d'utilisation rare, il peut donc être souvent ignoré.

+0

Je sais, j'utilise la colonne "sort_order" comme vous le dites, mais ce serait bien de ne pas écrire ces méthodes de mise à jour et de les automatiser en utilisant un comportement de doctrine. – enumag

3

Je voudrais l'implémenter en utilisant event system de Doctrine. Pour ajouter des comportements, j'écris généralement un abonné d'événement et applique les règles avec une interface implémentée par mes classes d'entité. La logique que je garde dans un objet de service quelque part (vous êtes seul pour cela).

use Doctrine\Common\EventSubscriber, 
    Doctrine\ORM\Events, 
    Doctrine\ORM\Event\LifecycleEventArgs, 
    Doctrine\ORM\Event\PreUpdateEventArgs; 


class SortableBehavior implements EventSubscriber 
{ 
    public function getSubscribedEvents() 
    { 
     return array(Events::prePersist, Events::preUpdate); 
    } 

    public function prePersist(LifeCycleEventsArgs $args) 
    { 
    // the entity being persisted 
    $entity = $args->getEntity(); 
    if ($entity instanceof SortableInterface) { 
     //perform sorting magic 
    } 
    } 

    public function preUpdate(preUpdateEventsArgs $args) 
    { 
    // the entity being updated 
    $entity = $args->getEntity(); 
    if ($entity instanceof SortableInterface) { 
     //perform sorting magic 
    } 
    } 
} 

Ne pas oublier d'enregistrer les abonnés quand Amorcer votre application:

$eventManager = $entityManager->getEventManager(); 
$eventManager->addEventSubscriber(new SortableBehavior()); 
+0

Donc la magie de tri est d'obtenir le référentiel, puis toutes les autres entités et d'incrémenter (ou décrémenter) la valeur sort_order pour certaines d'entre elles? Qu'en est-il des performances? Je devrais charger et enregistrer à nouveau toutes les entités. Sans Doctrine, je pourrais utiliser une seule requête UPDATE sql. – enumag

+0

Si vous souhaitez utiliser l'approche de requête UPDATE, vous pouvez utiliser l'objet NativeQuery de D2. En tant que ORM, l'approche par défaut de Doctrine serait de travailler avec vos entités, ce qui impliquerait de les récupérer d'abord dans la base de données. Les performances pourraient être améliorées en utilisant les différentes caches fournies par Doctrine. –

0

J'ai fait quelques essais avec le EventSubscriber mais n'a pas pu le faire fonctionner correctement alors j'ai décidé d'ajouter directement sur tout le code à ma classe (q & d). J'utilise Zend Framework donc change la récupération du gestionnaire d'entité si vous avez une solution différente.

Il fonctionne comme suit:

  1. Lors de la création de l'entité utilise la getLast() et ajouter la nouvelle entité dernière ($ last_entity-> getSort() + 1)
  2. Ajouter les fonctions sortUp et sortDown et leurs sous-routines au code de l'entité
  3. Ajouter « tri ASC » dans toutes vos requêtes DQL où vous souhaitez trié

le code de l'entité est la suivante:

/** 
* Searches for the element above and switches them 
* with eachother. Performs a EntityManager::flush() 
* to save the results after the switch. 
*/ 
public function sortUp() { 

    try { 
     $em = \Zend_Registry::get("entitymanager"); 

     $class_name = get_class($this); 
     $dql = "SELECT ut FROM $class_name ut WHERE ut.inactive IS NULL AND ut.inactive IS NULL AND ut.sort < '" . $this->getSort() . "' ORDER BY ut.sort DESC"; 
     $query = $em->createQuery($dql); 
     $query->setMaxResults(1); 
     $ut = $query->getResult(); 
    } catch (Exception $exc) { 
     throw new Exception("Error when looking for sortable partner: " . $exc->getMessage()); 
    } 

    if (count($ut)) { 
     $this->_switchSortAndSave($ut[0]); 
    } 
} 

/** 
* Searches for the element below and switches them 
* with eachother. Performs a EntityManager::flush() 
* to save the results after the switch. 
*/ 
public function sortDown() { 
    try { 
     $em = \Zend_Registry::get("entitymanager"); 

     $class_name = get_class($this); 
     $dql = "SELECT ut FROM $class_name ut WHERE ut.inactive IS NULL AND ut.sort > '" . $this->getSort() . "' ORDER BY ut.sort ASC"; 
     $query = $em->createQuery($dql); 
     $query->setMaxResults(1); 
     $ut = $query->getResult(); 
    } catch (Exception $exc) { 
     throw new Exception("Error when looking for sortable partner: " . $exc->getMessage()); 
    } 


    if (count($ut)) { 
     $this->_switchSortAndSave($ut[0]); 
    } 
} 

private function _switchSortAndSave(\Entities\Usertype $switch_entity) { 
    $new_sort = $switch_entity->getSort(); 
    $switch_entity->setSort($this->getSort()); 
    $this->setSort($new_sort); 

    $em = \Zend_Registry::get("entitymanager"); 
    $em->persist($switch_entity); 
    $em->persist($this); 
    $em->flush(); 
} 

/** 
* Looks for the last entry according to sort order 
* and returns that if found. 
* 
* @return Entity|null 
*/ 
public static function findLast() { 
    try { 
     $em = \Zend_Registry::get("entitymanager"); 

     $class_name = get_called_class(); 
     $dql = "SELECT ut FROM $class_name ut ORDER BY ut.sort DESC"; 
     $query = $em->createQuery($dql); 
     $query->setMaxResults(1); 
     $ut = $query->getResult(); 
    } catch (Exception $exc) { 
     throw new Exception("Error when searching for last $class_name: " . $exc->getMessage()); 
    } 

    if (count($ut)) 
     return $ut[0]; 
    else 
     return null; 
} 

Je ne suis pas satisfait de cette solution, donc si quelqu'un arrive avec une belle Sortable pour Doctrine 2 s'il vous plaît partager :)