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();
}