0

I would like to reorder the attribute (COMMENTS) of my object (instance of ARTICLE) after I retrieve it from the DBB. Is this possible?

My object is ARTICLE and it is linked to COMMENTS (which is defined as a collection in entity article)

I know I can order through the repository but the order of my comments depend on many conditions, some not available through the DB.

Condition exemple: I want at the top the comment whose attribute show_first are set to true whatever their score, and then the other comments ordered depending of their score.

AppyGG
  • 381
  • 1
  • 6
  • 12
Alexis_D
  • 1,908
  • 3
  • 16
  • 35
  • What sort of ordering are you thinking? You could create a hidden field in your repository call and order by that rather than needing to process a complete collection of objects each time. – qooplmao Jun 17 '15 at 11:41
  • @Qoop ok you mean create a attribut that sort the comment in the desired arrangement? – Alexis_D Jun 17 '15 at 12:46
  • Only in the query. I'll add an answer. – qooplmao Jun 17 '15 at 12:49

4 Answers4

1

Set the getter of comments (getComments()) in your Article entity to get the comments in the order you want.

public function getComments(){
    $iterator = $comments->getIterator();
    $iterator->uasort(function ($a, $b) {
         // change getProperty() with the field you want to order on 
         return ($a->getProperty() < $b->getProperty()) ? -1 : 1;
    });
    $comments= new ArrayCollection(iterator_to_array($iterator));
    return $comments;
}

For more Infos visit this post "usort" a Doctrine\Common\Collections\ArrayCollection?

Community
  • 1
  • 1
zizoujab
  • 7,603
  • 8
  • 41
  • 72
  • will the comments be ordered according to this function if I triggered Article along with comments in a query from a repository? – Alexis_D Jun 17 '15 at 12:54
  • I did not get what you want to do exactly, you can sort comments while getting them from repository ,or sort them in getter using my answer. You can do both too , it's up to your order logic – zizoujab Jun 17 '15 at 14:35
1

You could add a hidden field to you query that sorting things in the order that you wanted so that you don't need to process the complete ArrayCollection to sort.

public function findByArticleInOrderOfState()
{
    return $this->createQueryBuilder('c')
        ->select('c')
        ->addSelect('
            CASE 
                WHEN c.state = :state_new THEN 1
                WHEN c.state = :state_viewed THEN 2
                WHEN c.state = :state_deleted THEN 3
                ELSE 4
            END AS HIDDEN order_by
        ')
        ->setParameter('state_new', 'new')
        ->setParameter('state_viewed', 'viewed')
        ->setParameter('state_deleted', 'deleted')
        ->orderBy('order_by', 'ASC')
        ->addOrderBy('c.createdAt', 'ASC')
        ->getQuery()
        ->getResults();
}

This would create a hidden field order_by and set that depending on the current state of that object, then it would order by that hidden field and then createdAt.

It doesn't really make sense to order comments like that but it does show how you could do it. With a little more info on the actual use case I would (hopefully) be able to make work a bit closer to your specific needs.

Update

In your case when you have show_first == 'yes'|'no' you could do the following..

public function findByArticleInOrderOfState()
{
    return $this->createQueryBuilder('c')
        ->select('c')
        ->addSelect('
            CASE 
                WHEN c.show_first = :show_first THEN 1
                ELSE 2
            END AS HIDDEN order_by
        ')
        ->setParameter('show_first', 'yes')
        ->orderBy('order_by', 'ASC')
        ->addOrderBy('c.createdAt', 'ASC')
        ->getQuery()
        ->getResults();
}
qooplmao
  • 17,622
  • 2
  • 44
  • 69
  • ok thanks, I edited my question with exemple of the conditions. So to catch with my need, and if I understand your answer I could do **WHEN c.show_first = "true" THEN 1 ELSE 2. and then ->orderBy('order_by','ASC') ->addOrderBy('c.score','DESC')** – Alexis_D Jun 17 '15 at 13:12
  • Maybe just **CASE WHEN c.show_first THEN 1 ELSE 2 END AS HIDDEN order_by** as it's a boolean. – qooplmao Jun 17 '15 at 13:17
  • I got the following message from symfony **[Syntax Error] line 0, col 57: Error: Expected Literal, got '"'** – Alexis_D Jun 17 '15 at 13:54
  • I am triing your answer but sot SNTAX ERROR... I open an other post here [http://stackoverflow.com/questions/30894291/symfony-query-error-expected-literal-got] – Alexis_D Jun 17 '15 at 14:19
  • Maybe changing it for a named parameter like `WHEN c.show_first = :show_first THEN` and `->setParameter('show_first', 'yes')`. – qooplmao Jun 17 '15 at 14:47
  • yes it works now. thanks a lot. (why do I have to set parameters all the time... it is so confusing and the syntaxe not so easy because I tried before and would find the right way to do it.) Anyway thanks again I valide you answer because even if the one from zizoujab works also I find yours easier to understand and more logical to have it in my query. – Alexis_D Jun 17 '15 at 14:52
  • Glad it works. I guess the best way with the parameters is do everything with parameters and then refactor if there is an error thrown, rather than the other way around. I'll update my answer too. – qooplmao Jun 17 '15 at 14:59
0

For simple ordering of associations, you can use Doctrine annotations.

/**
 * @ORM\OneToMany(targetEntity="Comment", mappedBy="article")
 * @ORM\OrderBy({"show_first" = "ASC", "score" = "DESC"})
 */
private $comments;

https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/ordered-associations.html

Nepomuk Pajonk
  • 2,972
  • 1
  • 19
  • 29
0

The following possible within an entity object

    public function getCommentsActiveLast3()
    {
        $criteria = Criteria::create();
        $criteria->where(Criteria::expr()->eq('status', Comment::STATUS_PUBLISHED));
        $criteria->setMaxResults(3);

        if ($this->comments) {
            return $this->comments->matching($criteria);
        }
    }
Ivan Proskuryakov
  • 1,625
  • 2
  • 23
  • 32