Как делать сложные запросы используя репозитории и объединение разных таблиц?
 решением этой несложной задачки.
Уважаю такой оптимизм. 

Проблема эта не нова. Называется она object-relational impedance mismatch и очень многие считают её в принципе нерешаемой, сравнивая её с  проигранной США войной во Вьетнаме. 
Так что можно надеяться на что угодно, только не на простое решение. Но сначала надо проблему осознать. Что отображение объектов на реляционную базу, которое называется object-relational mapping, сокращённо ORM,  никогда не бывает простым.

Отдельно этой теме добавляет остроты терминология. Спроси 10 разных разработчиков что они имеют в виду под репозиторием и под маппингом, и получишь 20 разных мнений. Так что использовать красивые слова следует с очень большой осторожностью. 

К примеру, "нерешаемость" проблемы с impedance mismatch относится к попыткам сделать универсальный ORM, который на вход получает имя любого класса, а на выходе коллекцию объектов. Про такой вариант можно действительно забыть (привет, элоквент-элоквент - и в продакшен!). Но вот полуавтоматическое решение вполне можно накостылить. Главное всегда помнить о проблеме, и как только автоматический маппинг перестаёт работать - тут же от него оказываться в пользу ручного колупания с запросами. Главное этого не бояться и не загонять себя в клетку словами "репозиторий", "один объект-одна таблица" и пр. У тебя есть задача - инстанцировать объект или коллекци объектов из БД. Окей, ты пишешь методы, которые это делают оптимальным способом, не важно - одна там таблица используется, 10 или еще плюс 2 кэша и носкл датабаза в придачу. 
Надо тебе сохранить объект или коллекци объектов в БД? Окей, пишешь метод, коорый делает это оптимальным способом. Да, это куча черной работы. Но зато у тебя будет чистая доменная логика (которая вообще никакого отношения к базе данных или "репозиториям" не имеет).

Отдельно прекламирую Cycle ORM. Сам я ненастоящий сварщик, но взрослые дядьки говорят что она лучше всего подходит для нормально реализованного маппинга объектов на БД. Лучше чем Доктрина или прости-господи Элоквент. С нетерпением жду доклада автора на ПХПРаша.
Можно использовать представления со стороны БД, либо фабрики хранилищ со стороны кода.
Модно сначала просто статически запечь в отдельный класс, если это действительно узкое место. Если паттерн себя проявит, то можно оформить в виде фабрики.

Вон в C# в Entity Framework почти как вы сказали, только там запрос конструируется из коллекций классов, которые умеют в маппинг конкретной предметной области. Но там на выходе уже полноценная ORM с оптимизациями типа отложенного запроса к БД: технология Linq-To-Entity реализует реляционную логику, так что громоздкий SQL запрос модно отложить до самого последнего момента.