Вопрос: Как правильно работать с объектами выборки doctrine в Symfony?

Лучший ответ: дополню ответ Юрий Во первых, это на столько здоровый объект реляции images, что отдебажить его можно только с помощью функции dump. 
Все сущности заворачиваются в прокси объекты, что бы работала "магия" вроде ленивой подгрузки и т.д. Именно по этому в сущностях "больше" чем есть на самом деле.
По поводу коллекций, в Doctrine есть такая штука как Collection. Вы должны понимать что в доктрине вы оперируете не табличками в базе, а объектами. Строите именно объектную модель вашей системы. В этом ключе можете почитать что такое "агрегат сущностей". В вашем случае у вас агрегат будет состоять из двух сущностей. Product и его Image. Например если вы захотите сделать добавление картинок, вы можете сделать так:/**
 * usage: $product->addImage($image);
 */
public function addImage(Image $image)
{
    $this->images->add($image);
}
А коллекция сама выполнит persist новой сущности. Таким образом количество репозиториев уменьшается до количества корней агрегатов сущностей. В вашем примере "корнем", то есть вершиной графа взаимоотношений объектов в контексте продуктов, является сам продукт. А потому репозиторий мы будем делать только для продуктов. Все остальное внутри оного разруливается либо при помощи коллекций.
При работе с доктриной вообще полезно представлять себе, что никакой базы данных у вас нет. Что данные просто живут между запросами где-то там, в памяти. Это должно помочь вам "абстрагироваться" и перестать смешивать "сущности" и "таблицы".
К примеру "новички" в доктрине любят персисть сущность даже для обновления. Они путают `persist` и `save`. Так вот, если вы загрузили сущность из базы через доктрину, то сущность уже попадает в unit of work. И делать persist уже не нужно, этот метод только для того чтобы доктрина узнала о чем-то новом. А так она и так знает про эту сущность. В итоге вы можете просто что-то поменять и вызвать flush. То есть репозиторий - это тупо хранилище. Хранилище умеет хранить. Изменять то, что оно хранит оно не может.
Так же рекомендую на тему репозиториев почитать это:
www.whitewashing.de/2013/03/04/doctrine_repositori...
Ну и в целом.
https://www.youtube.com/watch?v=rzGeNYC3oz0 - доклад о том как готовить доктрину от авторов оной.
От себя лишь добавлю простые правила:
 - Не используйте напрямую доктриновские репозитории. Пишите свои, а в них уже юзайте доктриновские. Не стоит размазывать доктрину по всему проекту, потом это будет нереально поддерживать.
 - Не наследуйтесь от EntityRepository. Это внутренний механизм доктрины общего назначения. Используйте их в своих репозиториях со своим интерфейсом, повышая специфичность и ужесточая контроль за тем кто что юзает. 
 - Старайтесь использовать entity manager только в своих репозиториях и каких то небольших сервисах. Не размазывайте все по всюду.что очень сильно срет память.
Доктрина гарантирует вам что в памяти будет всегда только один инстанс сущности. То есть если у вас есть 10 объектов одного типа и имеющих один объект, это все будут ссылки на одну сущность. В вашем случае у вас просто циклическая ссылка между продуктами и изображениями. dump циклические ссылки не особо умеет.
Это логичное ограничение, дабы не возникало ситуаций что вы обновили что-то в одном экземпляре сущности и что-то в другом, и в базу попадут только часть изменений. За подробностями - читайте документацию к доктрине в отношении UnitOrWork и Identity Map.

Худший ответ: Если очень хочется именно массив сущностей:public function getImages()
{
   return $this->images->toArray();
}
Но в таком случае вы лишаетесь преимуществ доктриновской коллекции. Особенно это заметно при ленивой загрузке.
По остальному:Выворачивать ссылку на коллекцию в гетерах(делать return $this->images;) - неочень.
 Используйте return clone $this->images;setProductId() и подобные в вашем классе картинки бесполезен, у вас уже есть реляция.Может быть в тему в вашем случае - Extra Lazy Associations