Von_Kavalier Von_Kavalier - 5 months ago 14
PHP Question

Sort data and insertion that orders automatically - Algorithm

Well, I know I might get some downvotes for this question, but I really need help or I won't have any hair left in a few hours.

I got an array like this:

array(6) {
[0]=>
object((2) {
["nivId"]=>int(3)
["nivOrdre"]=>int(1)
}
[1]=>
object((2) {
["nivId"]=>int(4)
["nivOrdre"]=>int(2)
}
[2]=>
object((2) {
["nivId"]=>int(6)
["nivOrdre"]=>int(3)
}
[3]=>
object((2) {
["nivId"]=>int(2)
["nivOrdre"]=>int(4)
}
[4]=>
object((2) {
["nivId"]=>int(1)
["nivOrdre"]=>int(5)
}
[5]=>
object((2) {
["nivId"]=>int(5)
["nivOrdre"]=>int(6)
}
}


In my HTML I display them ordered by
nivOrdre


I can modify
nivOrdre
for each of them in the HTML and it changes in the db.

What I want to do is when I modify a
nivOrdre
, all the others that are higher get incremented by one.

I can't get the loops to work properly because of the
nivId
and
nivOrdre
, can't figure how to write that algorithm.

I also tried to not increment when there is a gap between two values.
My code has a lots of bugs and I am desperate to make it work someday...

Here is what I did :

public function modNiveaux($niveau) {
$niveaux = $this->getNiveauxRepository()->findBy(array(), array('nivOrdre' => 'ASC'));
$add = false; $ite=0;
for($i=$niveau->getNivOrdre(); $i<sizeof($niveaux); $i++) {
echo $niveau->getNivOrdre().':'.$niveaux[$i]->getNivOrdre().'<br/>';
if($niveau->getNivOrdre() != $niveaux[$i-1]->getNivOrdre() && $niveau->getNivOrdre() != $niveaux[$i-1]->getNivOrdre())
$add=true;
}

for($i=0; $i<sizeof($niveaux); $i++){
if($niveaux[$i]->getNivOrdre() == $niveau->getNivOrdre()){
$ite=$i;
}
}

if($add){
for($i=$ite; $i<=sizeof($niveaux)-1; $i++){
$niveaux[$i]->setNivOrdre($niveaux[$i]->getNivOrdre()+1);
$this->getEntityManager()->persist($niveaux[$i]);
}
}

$this->getEntityManager()->flush();
}


That code is in the
Service
and called in the
Controller
like that :

public function updateAction($id) {
$request = $this->get('request');
if (is_null($id)) {
$postData = $request->get('niveaux');
$id = $postData['id'];
}

$this->niveauxService = $this->get("intranet.niveaux_service");
$niveau = $this->niveauxService->getNiveau($id);

$form = $this->createForm(new NiveauxType(), $niveau);
$form->handleRequest($request);

if ($form->isValid()) {
$this->niveauxService->saveNiveau($niveau);
$this->niveauxService->modNiveaux($niveau);
$this->get('session')->getFlashBag()->add('notice', 'Objet sauvegardé avec succès');
} else {
$this->get('session')->getFlashBag()->add('noticeError', 'L\'objet n\'a pu être mis à jour.');
}

return array(
'form' => $form->createView(),
'id' => $id,
);
}


If someone has an idea to make it work I will be eternally thankful..

Answer

Basing on your question and comments all you want to do is to increment all niveaux with ordre greater or equal than the new value of changed entity.

Since the entity provided to modNiveaux method already has assigned new value, inside the service you need to retrieve entities with greater all equal ordre than the current (except the current!) and increment them.

The value of current entity is already changed by the form, so there's nothing to do with it.

That would be something like this:

public function modNiveaux($niveau) {
    $criteria = new \Doctrine\Common\Collections\Criteria();
    //greater or equal nivOrdre
    $criteria->where($criteria->expr()->gte('nivOrdre', $niveau->getNivOrdre()));
    //but not the current one
    $criteria->andWhere($criteria->expr()->neq('nivId', $niveau->getNivId()));

    $niveaux = $this->getNiveauxRepository()->matching($criteria);
    //increment all of them and persist
    foreach($niveaux as $item) {
        $item->setNivOrdre($item->getNivOrdre()+1);
        $this->getEntityManager()->persist($item);
    }

    $this->getEntityManager()->flush();
}

This code is of course not tested and may contain simple mistakes, but this is the idea.

Comments